ããã¯ã°ã©ãŠã³ã
ããåææ¥ã®å€ãç§ã¯åº§ã£ãŠãwebpackã䜿çšããŠUIããããäœæããæ¹æ³ãæ¢ããŠããŸãããç§ã¯styleguidstãUIãããã®ãã¢ãšããŠäœ¿çšããŠããŸãããã¡ãããwebpackã¯ã¹ããŒãã§ãäœæ¥ãã£ã¬ã¯ããªã«ãããã¹ãŠã®ãã¡ã€ã«ã1ã€ã®ãã³ãã«ã«è©°ã蟌ã¿ããããããã¹ãŠãé çªã«å€ãããŸãã
entry.jsãã¡ã€ã«ãäœæããããã«ãã¹ãŠã®ã³ã³ããŒãã³ããã€ã³ããŒãããŠããããããšã¯ã¹ããŒãããŸããããã¹ãŠã倧äžå€«ã®ããã§ãã
import Button from 'components/Button'
import Dropdown from 'components/Dropdown '
export {
Button,
Dropdown
}
ãããŠãããããã¹ãŠãæ§ç¯ããåŸãç§ã¯output.jsãååŸããŸãããäºæ³ã©ããããã¹ãŠã1ã€ã®ãã¡ã€ã«å ã®ããŒãå ã®ãã¹ãŠã®ã³ã³ããŒãã³ãã§ãããããã§è³ªåãçããŸããïŒ
ãã¹ãŠã®ãã¿ã³ãããããããŠã³ãªã©ãåå¥ã«åéããŠãä»ã®ãããžã§ã¯ãã«ã€ã³ããŒãããã«ã¯ã©ãããã°ããã§ããïŒããããç§ã¯ãããããã±ãŒãžãšããŠnpmã«ã¢ããããŒãããããšæããŸãã
ããŒã...é çªã«è¡ããŸãããã
è€æ°ã®ãšã³ããª
ãã¡ãããé ã«æµ®ãã¶æåã®ã¢ã€ãã¢ã¯ãäœæ¥ãã£ã¬ã¯ããªå ã®ãã¹ãŠã®ã³ã³ããŒãã³ããè§£æããããšã§ããNodeJSã䜿çšããããšã¯ãã£ãã«ãªãããããã¡ã€ã«ã®è§£æã«ã€ããŠå°ãã°ã°ãå¿ èŠããããŸãããglobã®ãããªãã®ãèŠã€ããŸããã
ç§ãã¡ã¯è€æ°ã®ãšã³ããªãæžãããã«é転ããŸããã
const { basename, join, resolve } = require("path");
const glob = require("glob");
const componentFileRegEx = /\.(j|t)s(x)?$/;
const sassFileRegEx = /\s[ac]ss$/;
const getComponentsEntries = (pattern) => {
const entries = {};
glob.sync(pattern).forEach(file => {
const outFile = basename (file);
const entryName = outFile.replace(componentFileRegEx, "");
entries[entryName] = join(__dirname, file);
})
return entries;
}
module.exports = {
entry: getComponentsEntries("./components/**/*.tsx"),
output: {
filename: "[name].js",
path: resolve(__dirname, "build")
},
module: {
rules: [
{
test: componentFileRegEx,
loader: "babel-loader",
exclude: /node_modules/
},
{
test: sassFileRegEx,
use: ["style-loader", "css-loader", "sass-loader"]
}
]
}
resolve: {
extensions: [".js", ".ts", ".tsx", ".jsx"],
alias: {
components: resolve(__dirname, "components")
}
}
}
å®äºãéããŸãã
ãã«ãåŸã2ã€ã®Button.jsãã¡ã€ã«ãDropdown.jsããã«ããã£ã¬ã¯ããªã«åé¡ãããŸãããå éšãèŠãŠã¿ãŸããããã©ã€ã»ã³ã¹ã®äžã«ã¯ãreact.production.min.jsãèªã¿ã«ãããããã¡ã€ãã³ãŒãããããŠããããã®ã§ãããããããŸããããŠããã¿ã³ã䜿ã£ãŠã¿ãŸãããã
ãã¿ã³ã®ãã¢ãã¡ã€ã«ã§ãã€ã³ããŒãããã«ããã£ã¬ã¯ããªããã€ã³ããŒãããããã«å€æŽããŸãã
ããã¯ãstyleguidistã®ãã¿ã³ã®ç°¡åãªãã¢ãã©ã®ããã«èŠãããã§ã-Button.md
```javascript
import Button from '../../build/Button'
<Button></Button>
```
IRãã¿ã³ãèŠã«è¡ããŸã... ãã®æ®µéã§ãwebpackãä»ããŠåéãããšããèããšæ¬²æ±ã¯ãã§ã«æ¶ããŠããŸãã
Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object.
webpackãªãã§å¥ã®ãã«ããã¹ãæ¢ããŠããŸã
ç§ãã¡ã¯ãŠã§ãããã¯ãªãã§ããã«ã«å©ããæ±ããŸããpackage.jsonã«ã¹ã¯ãªãããèšè¿°ããæ§æãã¡ã€ã«ãæ¡åŒµåãã³ã³ããŒãã³ããé 眮ãããŠãããã£ã¬ã¯ããªããã«ããããã£ã¬ã¯ããªãæå®ããŸãã
{
//...package.json -
scripts: {
"build": "babel --config-file ./.babelrc --extensions '.jsx, .tsx' ./components --out-dir ./build"
}
}
å®è¡ïŒ
npm run build
åºæ¥äžããããã«ããã£ã¬ã¯ããªã«Button.jsãDropdown.jsã®2ã€ã®ãã¡ã€ã«ãããããã¡ã€ã«å ã«ã¯çŸãããã¶ã€ã³ãããããã©js +ããã€ãã®ããªãã£ã«ãšå€ç¬ãªèŠæ±ïŒ "styles.scss"ïŒããããŸããæããã«ãããã¯ãã¢ã§ã¯æ©èœãããã¹ã¿ã€ã«ã®ã€ã³ããŒããåé€ãïŒãã®æç¹ã§ãscssãã©ã³ã¹ãã€ã«ã®ãã©ã°ã€ã³ãèŠã€ããããšãæåŸ ããŠããã£ãŠããŸããïŒãå床åéããŸãã
çµã¿ç«ãŠåŸããçŽ æµãªJSãããã€ããããŸããã¢ã»ã³ãã«ãããã³ã³ããŒãã³ããstyleguidistã«çµ±åããŠã¿ãŸãããã
```javascript
import Button from '../../build/Button'
<Button></Button>
```
ã³ã³ãã€ã«æžã¿-åäœããŸããã¹ã¿ã€ã«ã®ãªããã¿ã³ã®ã¿ã
ãã©ã³ã¹ãã€ã«scss / sassã®ãã©ã°ã€ã³ãæ¢ããŠããŸã
ã¯ããã³ã³ããŒãã³ãã®ã¢ã»ã³ããªã¯æ©èœããã³ã³ããŒãã³ãã¯æ©èœããŠããŸããnpmãŸãã¯ç¬èªã®ãã¯ãµã¹ã§ãã«ããå ¬éã§ããŸããããã§ããã¹ã¿ã€ã«ãä¿åããã ãã§ã... OKãGoogleãåã³ãµããŒãããŸãïŒãããïŒã
ãã©ã°ã€ã³ãã°ãŒã°ã«ã§æ€çŽ¢ããŠãçµæã¯åŸãããŸããã 1ã€ã®ãã©ã°ã€ã³ã¯ã¹ã¿ã€ã«ããæååãçæãããã1ã€ã¯ãŸã£ããæ©èœããããã¥ãŒãã€ã³ããŒãããå¿ èŠããããŸãããstyles.scssãããã¹ã¿ã€ã«ãã€ã³ããŒããã
å¯äžã®åžæã¯ãã®ãã©ã°ã€ã³ã§ããïŒbabel-plugin-transform-scss-import-to-stringããããããã¯ã¹ã¿ã€ã«ããæååãçæããã ãã§ãïŒãã...ç§ã¯äžã§èšããŸããããããŒ...ïŒããã®åŸããã¹ãŠãããã«æªåããGoogleã®6ããŒãžã«å°éããŸããïŒãããŠãæèšã¯ãã§ã«åå3æã§ãïŒããããŠãäœããèŠã€ããããã®ç¹å¥ãªãªãã·ã§ã³ã¯ãããŸãããã¯ãããããŠèããããšã¯äœããããŸãã-ç§ã®å Žåã§ã¯ãªãããããã²ã©ãããwebpack + sass-loaderããŸãã¯ä»ã®äœããç¥çµ...ç§ã¯äŒæ©ãåãããè¶ã飲ãããšã«ããŸãããç§ã¯ãŸã ç ããããããŸãããç§ããè¶ãäœã£ãŠããéãscss / sasstranspileã®ãã©ã°ã€ã³ãæžããšããã¢ã€ãã¢ããŸããŸãé ã«æµ®ãã³ãŸãããç ç³ãããæ··ããããŠããéãç§ã®é ã®äžã§æã ã¹ããŒã³ã鳎ãé¿ããŸããïŒãplaaginãæžããŠãã ããããäºè§£ããŸããããã©ã°ã€ã³ãäœæããŸãã
ãã©ã°ã€ã³ãèŠã€ãããŸãããç§ãã¡ã¯èªåèªèº«ãæžããŸã
ãã©ã°ã€ã³ã® åºç€ãšããŠãäžèšã®babel-plugin-transform-scss-import-to-stringã䜿çšããŸãããASTããªãŒã䜿ã£ãçæ žããã®ä»ã®ããªãã¯ãããããšãå®å šã«çè§£ããŸãããã§ã¯è¡ããŸãããã
äºåæºåãè¡ããŸããnode-sassãšpathãããã³ãã¡ã€ã«ãšæ¡åŒµåã®éåžžã®è¡ãå¿ èŠã§ããã¢ã€ãã¢ã¯ããã§ãïŒ
- ã€ã³ããŒãè¡ããã¹ã¿ã€ã«ãå«ããã¡ã€ã«ãžã®ãã¹ãååŸããŸã
- node-sassãä»ããŠã¹ã¿ã€ã«ãæååã«è§£æããŸãïŒbabel-plugin-transform-scss-import-to-stringã«æè¬ããŸãïŒ
- ã€ã³ããŒãããšã«ã¹ã¿ã€ã«ã¿ã°ãäœæããŸãïŒbabelãã©ã°ã€ã³ã¯ã€ã³ããŒãããšã«èµ·åãããŸãïŒ
- ãã¹ãŠã®ããããªããŒãã¹ããŒãºã§åããã®ãã¹ããŒããªãããã«ãäœæãããã¹ã¿ã€ã«ãäœããã®æ¹æ³ã§èå¥ããå¿
èŠããããŸããçŸåšã®ãã¡ã€ã«ã®å€ãšã¹ã¿ã€ã«ã·ãŒãã®ååã䜿çšããŠã屿§ïŒdata-sass-componentïŒãããã·ã¥ããŠã¿ãŸãããããã®ãããªãã®ããããŸãïŒ
<style data-sass-component="Button_style"> .button { display: flex; } </style>
ãã©ã°ã€ã³ãéçºããŠãããžã§ã¯ãã§ãã¹ãããããã«ãcomponentsãã£ã¬ã¯ããªãšåãã¬ãã«ã§ãbabel-plugin-transform-scssãã£ã¬ã¯ããªãäœæããããã«package.jsonãè©°ã蟌ã¿ãããã«libãã£ã¬ã¯ããªãè©°ã蟌ã¿ãŸããããããŠããã§ã«index.jsãããã«æå ¥ããŸããã
ããªãã¯vkurseã«ãªããŸã-Babelconfigã¯ãpackage.jsonã®mainãã£ã¬ã¯ãã£ãã§æå®ãããŠãããã©ââã°ã€ã³ã®åŸãã«ç»ããŸãããã®ãããç§ã¯ãããè©°ã蟌ãŸãªããã°ãªããŸããã§ãããç§ãã¡ã¯ç€ºããŸãïŒ
{
//...package.json - , main
main: "lib/index.js"
}
次ã«ããã©ã°ã€ã³ãžã®ãã¹ãbabel configïŒ.babelrcïŒã«ããã·ã¥ããŸãã
{
//
plugins: [
"./babel-plugin-transform-scss"
//
]
}
ããã§ã¯ãindex.jsã«éæ³ãè©°ã蟌ã¿ãŸãããã
æåã®æ®µéã¯ãscssãŸãã¯sassãã¡ã€ã«ã®ã€ã³ããŒãã確èªããã€ã³ããŒãããããã¡ã€ã«ã®ååãååŸããjsãã¡ã€ã«ïŒã³ã³ããŒãã³ãïŒèªäœã®ååãååŸããscssãŸãã¯sassæååãcssã«è»¢éããããšã§ããWebStormããnpmãŸã§ããããã¬ãŒãä»ããŠãã«ããå®è¡ãããã¬ãŒã¯ãã€ã³ããèšå®ãããã¹ãšç¶æ ã®åŒæ°ã調ã¹ãŠãã¡ã€ã«åããã§ããããcursesã§åŠçããŸãã
const { resolve, dirname, join } = require("path");
const { renderSync } = require("node-sass");
const regexps = {
sassFile: /([A-Za-z0-9]+).s[ac]ss/g,
sassExt: /\.s[ac]ss$/,
currentFile: /([A-Za-z0-9]+).(t|j)s(x)/g,
currentFileExt: /.(t|j)s(x)/g
};
function transformScss(babel) {
const { types: t } = babel;
return {
name: "babel-plugin-transform-scss",
visitor: {
ImportDeclaration(path, state) {
/**
* , scss/sass
*/
if (!regexps.sassExt.test(path.node.source.value)) return;
const sassFileNameMatch = path.node.source.value.match(
regexps.sassFile
);
/**
* scss/sass js
*/
const sassFileName = sassFileNameMatch[0].replace(regexps.sassExt, "");
const file = this.filename.match(regexps.currentFile);
const filename = `${file[0].replace(
regexps.currentFileExt,
""
)}_${sassFileName}`;
/**
*
* scss/sass , css
*/
const scssFileDirectory = resolve(dirname(state.file.opts.filename));
const fullScssFilePath = join(
scssFileDirectory,
path.node.source.value
);
const projectRoot = process.cwd();
const nodeModulesPath = join(projectRoot, "node_modules");
const sassDefaults = {
file: fullScssFilePath,
sourceMap: false,
includePaths: [nodeModulesPath, scssFileDirectory, projectRoot]
};
const sassResult = renderSync({ ...sassDefaults, ...state.opts });
const transpiledContent = sassResult.css.toString() || "";
}
}
}
ç«ãæåã®æåã¯ãtranspiledContentã§cssè¡ãååŸããŸãããæ¬¡ã«ãææªã®äºæ ã§ããASTããªãŒã®APIã«ã€ããŠãbabeljs.io / docs / en / babel - typesïŒapiã«ã¢ã¯ã»ã¹ããŸããastexplorer.netã«ã¢ã¯ã»ã¹ããŠãã¹ã¿ã€ã«ã·ãŒããé ã«æŒã蟌ãããã®ã³ãŒããèšè¿°ããŸãã
ã§astexplorer.netãã¹ã¿ã€ã«ã®ã€ã³ããŒãã®ä»£ããã«åŒã³åºãããèªå·±åŒã³åºã颿°ãæžããŸãïŒ
(function(){
const styles = "generated transpiledContent" // ".button {/n display: flex; /n}/n"
const fileName = "generated_attributeValue" //Button_style
const element = document.querySelector("style[data-sass-component='fileName']")
if(!element){
const styleBlock = document.createElement("style")
styleBlock.innerHTML = styles
styleBlock.setAttribute("data-sass-component", fileName)
document.head.appendChild(styleBlock)
}
})()
ASTãšã¯ã¹ãããŒã©ãŒã§ãå·ŠåŽã®è¡ã宣èšããªãã©ã«ãçªã-ããªãŒã®å³åŽã§å®£èšã®æ§é ã確èªãããã®æ§é ã䜿çšããŠbabeljs.io/docs/en/babel-types#apiã«ã¢ã¯ã»ã¹ããããããã¹ãŠã¹ã¢ãŒã¯ããŠã眮æãèšè¿°ããŸãã
æ°ååŸ...
1ã1.5æéåŸãastããbabel-types apiãŸã§ã¿ããå®è¡ããæ¬¡ã«ã³ãŒãã«ãscss / sassimportã®ä»£ãããäœæããŸãããastããªãŒãšbabel-typesapiãå¥ã ã«è§£æããããšã¯ããŸãããããã«å€ãã®æåããããŸããçµæãããã«è¡šç€ºããŸãã
const { resolve, dirname, join } = require("path");
const { renderSync } = require("node-sass");
const regexps = {
sassFile: /([A-Za-z0-9]+).s[ac]ss/g,
sassExt: /\.s[ac]ss$/,
currentFile: /([A-Za-z0-9]+).(t|j)s(x)/g,
currentFileExt: /.(t|j)s(x)/g
};
function transformScss(babel) {
const { types: t } = babel;
return {
name: "babel-plugin-transform-scss",
visitor: {
ImportDeclaration(path, state) {
/**
* , scss/sass
*/
if (!regexps.sassExt.test(path.node.source.value)) return;
const sassFileNameMatch = path.node.source.value.match(
regexps.sassFile
);
/**
* scss/sass js
*/
const sassFileName = sassFileNameMatch[0].replace(regexps.sassExt, "");
const file = this.filename.match(regexps.currentFile);
const filename = `${file[0].replace(
regexps.currentFileExt,
""
)}_${sassFileName}`;
/**
*
* scss/sass , css
*/
const scssFileDirectory = resolve(dirname(state.file.opts.filename));
const fullScssFilePath = join(
scssFileDirectory,
path.node.source.value
);
const projectRoot = process.cwd();
const nodeModulesPath = join(projectRoot, "node_modules");
const sassDefaults = {
file: fullScssFilePath,
sourceMap: false,
includePaths: [nodeModulesPath, scssFileDirectory, projectRoot]
};
const sassResult = renderSync({ ...sassDefaults, ...state.opts });
const transpiledContent = sassResult.css.toString() || "";
/**
* , AST Explorer
* replaceWith path.
*/
path.replaceWith(
t.callExpression(
t.functionExpression(
t.identifier(""),
[],
t.blockStatement(
[
t.variableDeclaration("const", [
t.variableDeclarator(
t.identifier("styles"),
t.stringLiteral(transpiledContent)
)
]),
t.variableDeclaration("const", [
t.variableDeclarator(
t.identifier("fileName"),
t.stringLiteral(filename)
)
]),
t.variableDeclaration("const", [
t.variableDeclarator(
t.identifier("element"),
t.callExpression(
t.memberExpression(
t.identifier("document"),
t.identifier("querySelector")
),
[
t.stringLiteral(
`style[data-sass-component='${filename}']`
)
]
)
)
]),
t.ifStatement(
t.unaryExpression("!", t.identifier("element"), true),
t.blockStatement(
[
t.variableDeclaration("const", [
t.variableDeclarator(
t.identifier("styleBlock"),
t.callExpression(
t.memberExpression(
t.identifier("document"),
t.identifier("createElement")
),
[t.stringLiteral("style")]
)
)
]),
t.expressionStatement(
t.assignmentExpression(
"=",
t.memberExpression(
t.identifier("styleBlock"),
t.identifier("innerHTML")
),
t.identifier("styles")
)
),
t.expressionStatement(
t.callExpression(
t.memberExpression(
t.identifier("styleBlock"),
t.identifier("setAttribute")
),
[
t.stringLiteral("data-sass-component"),
t.identifier("fileName")
]
)
),
t.expressionStatement(
t.callExpression(
t.memberExpression(
t.memberExpression(
t.identifier("document"),
t.identifier("head"),
false
),
t.identifier("appendChild"),
false
),
[t.identifier("styleBlock")]
)
)
],
[]
),
null
)
],
[]
),
false,
false
),
[]
)
);
}
}
}
æåŸã®åã³
ãã£ããŒ!!! ã€ã³ããŒãã¯ããã®ãã¿ã³ã䜿çšããŠã¹ã¿ã€ã«ãããã¥ã¡ã³ãã®å é ã«ããã·ã¥ãã颿°ã®åŒã³åºãã«çœ®ãæããããŸããããããŠãç§ããã®ã«ã€ãã¯å šäœããŠã§ãããã¯ããå§ããŠããµã¹ããŒããŒãåã£ããã©ããªãã ãããšæããŸãããããã¯æ©èœããŸããïŒããŠãåã£ãŠç¢ºèªããŸãããã®ãã¡ã€ã«ã¿ã€ãã®ããŒããŒãå®çŸ©ããå¿ èŠããããšãããšã©ãŒãåŸ ã£ãŠãWebpackã䜿çšããŠã¢ã»ã³ããªãèµ·åããŸã...ãããããšã©ãŒã¯ãªãããã¹ãŠãã¢ã»ã³ãã«ãããŸããããŒãžãéããŠèŠãŠã¿ããšãã¹ã¿ã€ã«ãããã¥ã¡ã³ãã®å é ã«è²Œãä»ããŠããŸããé¢çœãããšã«ã3ã€ã®ã¹ã¿ã€ã«ã®ããŒããŒãåé€ããŸããïŒãšãŠã幞ããªç¬é¡ïŒã
ãã®èšäºã«èå³ãããå Žåã¯ãgithubã®ã¢ã¹ã¿ãªã¹ã¯ã§ãµããŒãããŠãã ããã
npmããã±ãŒãžãžã®ãªã³ã¯ããããŸãïŒwww.npmjs.com/package/babel-plugin-transform-scss
泚ïŒèšäºã®å€ã«ãã¿ã€ãããšã«ã¹ã¿ã€ã«ãã€ã³ããŒãããããã®ãã§ãã¯ã远å ããŸãã'./styles.scss'ããã¹ã¿ã€ã«ãã€ã³ããŒãããŸã