ember-scoped-css 0.0.0 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +20 -2
- package/package.json +24 -71
- package/src/addon-css-rollup.js +92 -0
- package/src/addon-hbs-rollup.js +42 -0
- package/src/addon-js-unplugin.js +55 -0
- package/src/addon-rewritecss-rollup.js +29 -0
- package/src/app-css-livereload-loader.js +111 -0
- package/src/app-css-loader.js +28 -0
- package/src/app-css-unplugin.js +35 -0
- package/src/app-dependency-loader.js +19 -0
- package/src/app-js-unplugin.js +130 -0
- package/src/app-scopedcss-webpack.js +68 -0
- package/src/findCssInJs.js +27 -0
- package/src/fsExists.js +10 -0
- package/src/getClassesTagsFromCss.js +31 -0
- package/src/getFiles.js +12 -0
- package/src/getImportedCssFiles.js +18 -0
- package/src/getPostfix.js +9 -0
- package/src/isInsideGlobal.js +6 -0
- package/src/replaceGlimmerAst.js +56 -0
- package/src/replaceHbsInJs.js +32 -0
- package/src/rewriteCss.js +40 -0
- package/src/rewriteHbs.js +39 -0
- package/src/rollup-ember-template-imports-plugin.js +77 -0
- package/test/getClassesTagsFromCss.js +15 -0
- package/test/getPostfix.js +23 -0
- package/test/rewriteCss.js +27 -0
- package/README.md +0 -32
package/index.js
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
|
-
|
|
1
|
+
const rollupEmberTemplateImportsPlugin = require('./src/rollup-ember-template-imports-plugin');
|
|
2
|
+
const addonJsUnplugin = require('./src/addon-js-unplugin');
|
|
3
|
+
const addonCssRollup = require('./src/addon-css-rollup');
|
|
4
|
+
const addonHbsRollup = require('./src/addon-hbs-rollup');
|
|
5
|
+
const appJsUnplugin = require('./src/app-js-unplugin');
|
|
6
|
+
const appCssUnplugin = require('./src/app-css-unplugin');
|
|
7
|
+
const appCssLoader = require('./src/app-css-loader');
|
|
8
|
+
const appDependencyLoader = require('./src/app-dependency-loader');
|
|
9
|
+
const appScopedcssWebpack = require('./src/app-scopedcss-webpack');
|
|
10
|
+
const addonRewritecssRollup = require('./src/addon-rewritecss-rollup');
|
|
2
11
|
|
|
3
12
|
module.exports = {
|
|
4
|
-
|
|
13
|
+
rollupEmberTemplateImportsPlugin,
|
|
14
|
+
addonJsUnplugin,
|
|
15
|
+
addonCssRollup,
|
|
16
|
+
addonHbsRollup,
|
|
17
|
+
appJsUnplugin,
|
|
18
|
+
appCssUnplugin,
|
|
19
|
+
appCssLoader,
|
|
20
|
+
appDependencyLoader,
|
|
21
|
+
appScopedcssWebpack,
|
|
22
|
+
addonRewritecssRollup,
|
|
5
23
|
};
|
package/package.json
CHANGED
|
@@ -1,80 +1,33 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ember-scoped-css",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
5
|
-
"
|
|
6
|
-
|
|
7
|
-
],
|
|
8
|
-
"repository": "",
|
|
9
|
-
"license": "MIT",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "",
|
|
5
|
+
"type": "commonjs",
|
|
6
|
+
"main": "index.js",
|
|
7
|
+
"keywords": [],
|
|
10
8
|
"author": "",
|
|
11
|
-
"
|
|
12
|
-
"doc": "doc",
|
|
13
|
-
"test": "tests"
|
|
14
|
-
},
|
|
15
|
-
"scripts": {
|
|
16
|
-
"build": "ember build --environment=production",
|
|
17
|
-
"lint": "concurrently \"npm:lint:*(!fix)\" --names \"lint:\"",
|
|
18
|
-
"lint:fix": "concurrently \"npm:lint:*:fix\" --names \"fix:\"",
|
|
19
|
-
"lint:hbs": "ember-template-lint .",
|
|
20
|
-
"lint:hbs:fix": "ember-template-lint . --fix",
|
|
21
|
-
"lint:js": "eslint . --cache",
|
|
22
|
-
"lint:js:fix": "eslint . --fix",
|
|
23
|
-
"start": "ember serve",
|
|
24
|
-
"test": "concurrently \"npm:lint\" \"npm:test:*\" --names \"lint,test:\"",
|
|
25
|
-
"test:ember": "ember test",
|
|
26
|
-
"test:ember-compatibility": "ember try:each"
|
|
27
|
-
},
|
|
9
|
+
"license": "ISC",
|
|
28
10
|
"dependencies": {
|
|
29
|
-
"
|
|
30
|
-
"
|
|
11
|
+
"blueimp-md5": "^2.19.0",
|
|
12
|
+
"cheerio": "1.0.0-rc.12",
|
|
13
|
+
"ember-source": "^4.10.0",
|
|
14
|
+
"ember-template-imports": "github:candunaj/ember-template-imports#embed-styles-in-gjs",
|
|
15
|
+
"ember-template-recast": "^6.1.3",
|
|
16
|
+
"glob": "^8.1.0",
|
|
17
|
+
"postcss": "^8.4.21",
|
|
18
|
+
"postcss-selector-parser": "^6.0.11",
|
|
19
|
+
"recast": "^0.22.0",
|
|
20
|
+
"rollup": "^2.67.0",
|
|
21
|
+
"unplugin": "^1.0.1"
|
|
31
22
|
},
|
|
32
23
|
"devDependencies": {
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"@embroider/test-setup": "^2.0.2",
|
|
37
|
-
"@glimmer/component": "^1.1.2",
|
|
38
|
-
"@glimmer/tracking": "^1.1.2",
|
|
39
|
-
"babel-eslint": "^10.1.0",
|
|
40
|
-
"broccoli-asset-rev": "^3.0.0",
|
|
41
|
-
"concurrently": "^7.6.0",
|
|
42
|
-
"ember-auto-import": "^2.5.0",
|
|
43
|
-
"ember-cli": "~4.10.0",
|
|
44
|
-
"ember-cli-dependency-checker": "^3.3.1",
|
|
45
|
-
"ember-cli-inject-live-reload": "^2.1.0",
|
|
46
|
-
"ember-cli-sri": "^2.1.1",
|
|
47
|
-
"ember-cli-terser": "^4.0.2",
|
|
48
|
-
"ember-load-initializers": "^2.1.2",
|
|
49
|
-
"ember-page-title": "^7.0.0",
|
|
50
|
-
"ember-qunit": "^6.1.1",
|
|
51
|
-
"ember-resolver": "^10.0.0",
|
|
52
|
-
"ember-source": "~4.10.0",
|
|
53
|
-
"ember-source-channel-url": "^3.0.0",
|
|
54
|
-
"ember-template-lint": "^5.3.1",
|
|
55
|
-
"ember-try": "^2.0.0",
|
|
56
|
-
"eslint": "^7.32.0",
|
|
57
|
-
"eslint-config-prettier": "^8.6.0",
|
|
58
|
-
"eslint-plugin-ember": "^11.4.3",
|
|
59
|
-
"eslint-plugin-n": "^15.6.1",
|
|
60
|
-
"eslint-plugin-prettier": "^4.2.1",
|
|
61
|
-
"eslint-plugin-qunit": "^7.3.4",
|
|
62
|
-
"loader.js": "^4.7.0",
|
|
63
|
-
"prettier": "^2.8.3",
|
|
64
|
-
"qunit": "^2.19.3",
|
|
65
|
-
"qunit-dom": "^2.0.0",
|
|
24
|
+
"chai": "^4.3.7",
|
|
25
|
+
"mocha": "^10.2.0",
|
|
26
|
+
"sinon": "^15.0.1",
|
|
66
27
|
"webpack": "^5.75.0"
|
|
67
28
|
},
|
|
68
|
-
"
|
|
69
|
-
"
|
|
70
|
-
|
|
71
|
-
"engines": {
|
|
72
|
-
"node": "14.* || 16.* || >= 18"
|
|
73
|
-
},
|
|
74
|
-
"ember": {
|
|
75
|
-
"edition": "octane"
|
|
76
|
-
},
|
|
77
|
-
"ember-addon": {
|
|
78
|
-
"configPath": "tests/dummy/config"
|
|
29
|
+
"scripts": {
|
|
30
|
+
"test": "mocha",
|
|
31
|
+
"lint": ""
|
|
79
32
|
}
|
|
80
|
-
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const getPostfix = require('./getPostfix');
|
|
3
|
+
const rewriteCss = require('./rewriteCss');
|
|
4
|
+
const { readFile } = require('fs').promises;
|
|
5
|
+
const fsExists = require('./fsExists');
|
|
6
|
+
const findCssInJs = require('./findCssInJs');
|
|
7
|
+
const getImportedCssFiles = require('./getImportedCssFiles');
|
|
8
|
+
const recast = require('recast');
|
|
9
|
+
|
|
10
|
+
module.exports = function () {
|
|
11
|
+
return {
|
|
12
|
+
name: 'addon-css-rollup',
|
|
13
|
+
|
|
14
|
+
async resolveId(source, importer, options) {
|
|
15
|
+
// catch emited css files
|
|
16
|
+
if (source.endsWith('.css')) {
|
|
17
|
+
const resolution = await this.resolve(source, importer, {
|
|
18
|
+
...options,
|
|
19
|
+
skipSelf: true,
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
if (resolution) {
|
|
23
|
+
return resolution;
|
|
24
|
+
} else {
|
|
25
|
+
const gjsCss = this.getModuleInfo(importer)?.meta?.gjsCss;
|
|
26
|
+
if (gjsCss) {
|
|
27
|
+
return {
|
|
28
|
+
external: false,
|
|
29
|
+
id: importer.replace(/\.js$/, '.css'),
|
|
30
|
+
meta: {
|
|
31
|
+
importer,
|
|
32
|
+
gjsCss,
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return null;
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
async load(id) {
|
|
42
|
+
if (id.endsWith('.css')) {
|
|
43
|
+
const gjsCss = this.getModuleInfo(id).meta.gjsCss;
|
|
44
|
+
let css = gjsCss;
|
|
45
|
+
if (!css) {
|
|
46
|
+
css = await readFile(id, 'utf-8');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return css;
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
async transform(code, id) {
|
|
54
|
+
if (id.endsWith('.css')) {
|
|
55
|
+
this.emitFile({
|
|
56
|
+
type: 'asset',
|
|
57
|
+
fileName: id.replace(path.join(process.cwd(), 'src/'), ''),
|
|
58
|
+
source: code,
|
|
59
|
+
});
|
|
60
|
+
return '';
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
|
|
64
|
+
generateBundle(a, bundle) {
|
|
65
|
+
let cssFiles = [];
|
|
66
|
+
for (let asset in bundle) {
|
|
67
|
+
const cssAsset = asset.replace('js', 'css');
|
|
68
|
+
if (!asset.endsWith('js') || !bundle[cssAsset]) {
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (process.env.environment === 'development') {
|
|
73
|
+
cssFiles.push(bundle[cssAsset].source);
|
|
74
|
+
delete bundle[cssAsset];
|
|
75
|
+
} else {
|
|
76
|
+
// add import to js files
|
|
77
|
+
bundle[asset].code =
|
|
78
|
+
`import './${path.basename(asset.replace('.js', '.css'))}';\n` +
|
|
79
|
+
bundle[asset].code;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (process.env.environment === 'development') {
|
|
84
|
+
this.emitFile({
|
|
85
|
+
type: 'asset',
|
|
86
|
+
fileName: 'scoped.css',
|
|
87
|
+
source: cssFiles.join('\n'),
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
const { readFile } = require('fs').promises;
|
|
2
|
+
const getPostfix = require('./getPostfix');
|
|
3
|
+
const replaceHbsInJs = require('./replaceHbsInJs');
|
|
4
|
+
const getClassesTagsFromCss = require('./getClassesTagsFromCss');
|
|
5
|
+
const rewriteHbs = require('./rewriteHbs');
|
|
6
|
+
const fsExists = require('./fsExists');
|
|
7
|
+
|
|
8
|
+
module.exports = function rollupCssColocation(options = {}) {
|
|
9
|
+
return {
|
|
10
|
+
name: 'addon-hbs-rollup',
|
|
11
|
+
|
|
12
|
+
async transform(code, id) {
|
|
13
|
+
if (id.endsWith('.hbs.js')) {
|
|
14
|
+
const hbsPath = id.replace('.js', '');
|
|
15
|
+
const cssPath = hbsPath.replace('.hbs', '.css');
|
|
16
|
+
|
|
17
|
+
const cssExists = await fsExists(cssPath);
|
|
18
|
+
if (cssExists) {
|
|
19
|
+
// read the css file
|
|
20
|
+
// TODO: get css from loader, because there are classes in imported css files; css can be stored in meta!!!!!
|
|
21
|
+
// const resolution = await this.resolve(importPath, id);
|
|
22
|
+
// resolution.meta.internalImport = true;
|
|
23
|
+
// const importedCss = await this.load(resolution);
|
|
24
|
+
const css = await readFile(cssPath, 'utf-8');
|
|
25
|
+
const { classes, tags } = getClassesTagsFromCss(css);
|
|
26
|
+
|
|
27
|
+
// generate unique postfix
|
|
28
|
+
const postfix = getPostfix(cssPath);
|
|
29
|
+
|
|
30
|
+
// rewrite the template
|
|
31
|
+
const rewrittenHbsJs = replaceHbsInJs(code, (hbs) => {
|
|
32
|
+
// add dependency to the css file
|
|
33
|
+
this.addWatchFile(cssPath);
|
|
34
|
+
return rewriteHbs(hbs, classes, tags, postfix);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
return rewrittenHbsJs;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
const { createUnplugin } = require('unplugin');
|
|
2
|
+
const { readFile } = require('fs').promises;
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const getClassesTagsFromCss = require('./getClassesTagsFromCss');
|
|
5
|
+
const getPostfix = require('./getPostfix');
|
|
6
|
+
const replaceHbsInJs = require('./replaceHbsInJs');
|
|
7
|
+
const rewriteHbs = require('./rewriteHbs');
|
|
8
|
+
const fsExists = require('./fsExists');
|
|
9
|
+
const findCssInJs = require('./findCssInJs');
|
|
10
|
+
const recast = require('recast');
|
|
11
|
+
|
|
12
|
+
module.exports = createUnplugin((options) => {
|
|
13
|
+
return {
|
|
14
|
+
name: 'addon-js-unplugin',
|
|
15
|
+
|
|
16
|
+
transformInclude(id) {
|
|
17
|
+
return id.endsWith('.js');
|
|
18
|
+
},
|
|
19
|
+
|
|
20
|
+
async transform(code, jsPath) {
|
|
21
|
+
const cssPath = jsPath.replace(/(\.hbs)?\.js$/, '.css');
|
|
22
|
+
const cssFileName = path.basename(cssPath);
|
|
23
|
+
|
|
24
|
+
const cssExists = await fsExists(cssPath);
|
|
25
|
+
let css;
|
|
26
|
+
if (cssExists) {
|
|
27
|
+
css = await readFile(cssPath, 'utf8');
|
|
28
|
+
} else {
|
|
29
|
+
if (code.includes('__GLIMMER_STYLES')) {
|
|
30
|
+
const result = findCssInJs(code, true);
|
|
31
|
+
css = result.css;
|
|
32
|
+
code = recast.print(result.ast).code;
|
|
33
|
+
this.getModuleInfo(jsPath).meta.gjsCss = result.css;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (!css) {
|
|
38
|
+
return code;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// add css import for js and gjs files
|
|
42
|
+
code = `import './${cssFileName}';\n\n${code}`;
|
|
43
|
+
|
|
44
|
+
// rewrite hbs in js in case it is gjs file (for gjs files hbs is already in js file)
|
|
45
|
+
// for js components "@embroider/addon-dev/template-colocation-plugin", will add hbs to js later. So there is hbs plugin to rewrite hbs
|
|
46
|
+
|
|
47
|
+
return replaceHbsInJs(code, (hbs) => {
|
|
48
|
+
const { classes, tags } = getClassesTagsFromCss(css);
|
|
49
|
+
const postfix = getPostfix(cssPath);
|
|
50
|
+
const rewritten = rewriteHbs(hbs, classes, tags, postfix);
|
|
51
|
+
return rewritten;
|
|
52
|
+
});
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const getPostfix = require('./getPostfix');
|
|
3
|
+
const rewriteCss = require('./rewriteCss');
|
|
4
|
+
const fsExists = require('./fsExists');
|
|
5
|
+
|
|
6
|
+
module.exports = function () {
|
|
7
|
+
return {
|
|
8
|
+
name: 'addon-rewritecss-rollup',
|
|
9
|
+
|
|
10
|
+
async transform(code, id) {
|
|
11
|
+
if (!id.endsWith('.css')) {
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
const postfix = getPostfix(id);
|
|
15
|
+
const jsPath = id.replace(/\.css$/, '.gjs');
|
|
16
|
+
const hbsPath = id.replace(/\.css$/, '.hbs');
|
|
17
|
+
|
|
18
|
+
const [jsExists, hbsExists] = await Promise.all([
|
|
19
|
+
fsExists(jsPath),
|
|
20
|
+
fsExists(hbsPath),
|
|
21
|
+
]);
|
|
22
|
+
|
|
23
|
+
if (jsExists || hbsExists) {
|
|
24
|
+
const rewritten = rewriteCss(code, postfix, path.basename(id));
|
|
25
|
+
return rewritten;
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
};
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
const { createUnplugin } = require('unplugin');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const { readFile, writeFile } = require('fs').promises;
|
|
4
|
+
const { Compilation } = require('webpack');
|
|
5
|
+
const getPostfix = require('./getPostfix');
|
|
6
|
+
const cheerio = require('cheerio');
|
|
7
|
+
|
|
8
|
+
module.exports = createUnplugin(({ appDir, loaders, htmlEntrypointInfo }) => {
|
|
9
|
+
return {
|
|
10
|
+
name: 'app-css-livereload-loader',
|
|
11
|
+
|
|
12
|
+
transformInclude(id) {
|
|
13
|
+
return id.endsWith('.js') || id.endsWith('.hbs');
|
|
14
|
+
},
|
|
15
|
+
|
|
16
|
+
async transform(code, jsPath) {
|
|
17
|
+
if (process.env.EMBER_ENV === 'production') {
|
|
18
|
+
return code;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const importRegex = /import\s+['"]([^'"]+\.css)['"]\s*;$/gm;
|
|
22
|
+
let cssPaths = [];
|
|
23
|
+
let match;
|
|
24
|
+
while ((match = importRegex.exec(code))) {
|
|
25
|
+
const importPath = match[1];
|
|
26
|
+
const directory = path.dirname(jsPath);
|
|
27
|
+
const cssPath = path.resolve(directory, importPath);
|
|
28
|
+
cssPaths.push(cssPath);
|
|
29
|
+
|
|
30
|
+
// replace import with empty string
|
|
31
|
+
code = code.replace(match[0], '');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (!cssPaths.length) {
|
|
35
|
+
return code;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const promises = cssPaths.map(async (cssPath) => {
|
|
39
|
+
let css = await readFile(cssPath, 'utf8');
|
|
40
|
+
for (let i = loaders.length - 1; i >= 0; i--) {
|
|
41
|
+
const loader = loaders[i];
|
|
42
|
+
css = await loader.bind({ resourcePath: cssPath })(css);
|
|
43
|
+
}
|
|
44
|
+
// random string; lenght is 8
|
|
45
|
+
const postfix = getPostfix(path.basename(cssPath));
|
|
46
|
+
|
|
47
|
+
this.emitFile({
|
|
48
|
+
type: 'asset',
|
|
49
|
+
fileName:
|
|
50
|
+
'assets/includedscripts/' + postfix + '_' + path.basename(cssPath),
|
|
51
|
+
source: css,
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
await Promise.all(promises);
|
|
56
|
+
|
|
57
|
+
return code;
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
webpack(compiler) {
|
|
61
|
+
if (process.env.EMBER_ENV === 'production') {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
compiler.hooks.thisCompilation.tap('Replace', (compilation) => {
|
|
66
|
+
compilation.hooks.processAssets.tapAsync(
|
|
67
|
+
{
|
|
68
|
+
name: 'Replace',
|
|
69
|
+
stage: Compilation.PROCESS_ASSETS_STAGE_DERIVED,
|
|
70
|
+
},
|
|
71
|
+
async (assets, callback) => {
|
|
72
|
+
try {
|
|
73
|
+
const cssAssets = Object.keys(assets).filter((asset) =>
|
|
74
|
+
asset.startsWith('assets/includedscripts/')
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
if (!cssAssets.length) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
let linkAdded = false;
|
|
81
|
+
const document =
|
|
82
|
+
htmlEntrypointInfo.htmlEntryPoint.dom.window.document;
|
|
83
|
+
|
|
84
|
+
for (let asset of cssAssets) {
|
|
85
|
+
const head = document.getElementsByTagName('head')[0];
|
|
86
|
+
const linkExists = head.querySelector(
|
|
87
|
+
`link[rel="stylesheet"][href="/${asset}"]`
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
if (!linkExists) {
|
|
91
|
+
const link = document.createElement('link');
|
|
92
|
+
link.rel = 'stylesheet';
|
|
93
|
+
link.href = '/' + asset;
|
|
94
|
+
head.appendChild(link);
|
|
95
|
+
linkAdded = true;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// if (linkAdded) {
|
|
100
|
+
// const indexHtmlWithLinks = dom.serialize();
|
|
101
|
+
// await writeFile(indexHtmlPath, indexHtmlWithLinks, 'utf8');
|
|
102
|
+
// }
|
|
103
|
+
} finally {
|
|
104
|
+
callback();
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
);
|
|
108
|
+
});
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// const { createUnplugin } = require('unplugin');
|
|
2
|
+
const { basename, join } = require('path');
|
|
3
|
+
const fsExists = require('./fsExists');
|
|
4
|
+
const getPostfix = require('./getPostfix');
|
|
5
|
+
const rewriteCss = require('./rewriteCss');
|
|
6
|
+
// const path = require('path');
|
|
7
|
+
|
|
8
|
+
module.exports = async function (code) {
|
|
9
|
+
const cssPath = this.resourcePath;
|
|
10
|
+
const cssFileName = basename(cssPath);
|
|
11
|
+
const postfix = getPostfix(cssPath);
|
|
12
|
+
|
|
13
|
+
const hbsPath = cssPath.replace('.css', '.hbs');
|
|
14
|
+
const gjsPath = cssPath.replace('.css', '.js');
|
|
15
|
+
const [hbsExists, gjsExists] = await Promise.all([
|
|
16
|
+
fsExists(hbsPath),
|
|
17
|
+
fsExists(gjsPath),
|
|
18
|
+
]);
|
|
19
|
+
|
|
20
|
+
let rewrittenCss;
|
|
21
|
+
if (hbsExists || gjsExists && cssPath.startsWith(this.rootContext)) {
|
|
22
|
+
rewrittenCss = rewriteCss(code, postfix, cssFileName);
|
|
23
|
+
} else {
|
|
24
|
+
rewrittenCss = code;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return rewrittenCss;
|
|
28
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
const { createUnplugin } = require('unplugin');
|
|
2
|
+
const replaceGlimmerAst = require('./replaceGlimmerAst');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const getPostfix = require('./getPostfix');
|
|
5
|
+
const getClassesTagsFromCss = require('./getClassesTagsFromCss');
|
|
6
|
+
const { readFile } = require('fs').promises;
|
|
7
|
+
const fsExists = require('./fsExists');
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
module.exports = createUnplugin(() => {
|
|
11
|
+
return {
|
|
12
|
+
name: 'app-css-unplugin',
|
|
13
|
+
|
|
14
|
+
// loadInclude(id){
|
|
15
|
+
// if(id.endsWith('.css')){
|
|
16
|
+
// return true;
|
|
17
|
+
// }
|
|
18
|
+
// },
|
|
19
|
+
|
|
20
|
+
// load(id){
|
|
21
|
+
// // this.addWatchFile(id);
|
|
22
|
+
// return 'console.log(' + JSON.stringify(id) + ');';
|
|
23
|
+
// },
|
|
24
|
+
|
|
25
|
+
// transformInclude(id) {
|
|
26
|
+
// if(id.endsWith('.css')){
|
|
27
|
+
// return true;
|
|
28
|
+
// }
|
|
29
|
+
// },
|
|
30
|
+
|
|
31
|
+
// async transform(code, id) {
|
|
32
|
+
// return code;
|
|
33
|
+
// },
|
|
34
|
+
};
|
|
35
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
const fsExists = require('./fsExists');
|
|
2
|
+
|
|
3
|
+
module.exports = async function (source) {
|
|
4
|
+
if (this.resourcePath.endsWith('.js')) {
|
|
5
|
+
const hbsExists = await fsExists(this.resourcePath.replace(/\.js/, '.hbs'));
|
|
6
|
+
if (hbsExists) {
|
|
7
|
+
return source;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const cssPath = this.resourcePath.replace(/(\.js)|(\.hbs)/, '.css');
|
|
12
|
+
const cssExists = await fsExists(cssPath);
|
|
13
|
+
|
|
14
|
+
if (cssExists) {
|
|
15
|
+
this.addDependency(cssPath);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return source;
|
|
19
|
+
};
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
const { createUnplugin } = require('unplugin');
|
|
2
|
+
const replaceGlimmerAst = require('./replaceGlimmerAst');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const getPostfix = require('./getPostfix');
|
|
5
|
+
const getClassesTagsFromCss = require('./getClassesTagsFromCss');
|
|
6
|
+
const { readFile } = require('fs').promises;
|
|
7
|
+
const fsExists = require('./fsExists');
|
|
8
|
+
|
|
9
|
+
function* iterateOpcodes(opcodes) {
|
|
10
|
+
for (let instruction of opcodes) {
|
|
11
|
+
if (!Array.isArray(instruction)) {
|
|
12
|
+
continue;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
yield instruction;
|
|
16
|
+
|
|
17
|
+
for (let subInstruction of iterateOpcodes(instruction)) {
|
|
18
|
+
yield subInstruction;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function inflateTagName(tag) {
|
|
24
|
+
if (typeof tag === 'string') {
|
|
25
|
+
return tag;
|
|
26
|
+
} else {
|
|
27
|
+
if (tag === 0) {
|
|
28
|
+
return 'div';
|
|
29
|
+
} else if (tag === 1) {
|
|
30
|
+
return 'span';
|
|
31
|
+
} else if (tag === 2) {
|
|
32
|
+
return 'p';
|
|
33
|
+
} else if (tag === 3) {
|
|
34
|
+
return 'a';
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
throw new Error('Unknown tag');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
module.exports = createUnplugin(({ appDir }) => {
|
|
41
|
+
return {
|
|
42
|
+
name: 'app-js-unplugin',
|
|
43
|
+
|
|
44
|
+
transformInclude(id) {
|
|
45
|
+
return (
|
|
46
|
+
id.includes(path.basename(appDir)) &&
|
|
47
|
+
(id.endsWith('.js') || id.endsWith('.hbs'))
|
|
48
|
+
);
|
|
49
|
+
},
|
|
50
|
+
|
|
51
|
+
async transform(code, id) {
|
|
52
|
+
const cssPath = id.replace(/(\.js)|(\.hbs)/, '.css');
|
|
53
|
+
const cssFileName = path.basename(cssPath);
|
|
54
|
+
const postfix = getPostfix(cssPath);
|
|
55
|
+
|
|
56
|
+
return await replaceGlimmerAst(code, id, (opcodes, css) => {
|
|
57
|
+
const { classes, tags } = getClassesTagsFromCss(css);
|
|
58
|
+
const a = code;
|
|
59
|
+
// this.addWatchFile(cssPath);
|
|
60
|
+
const tmp = this;
|
|
61
|
+
const insertions = [];
|
|
62
|
+
|
|
63
|
+
for (let instruction of iterateOpcodes(opcodes[0])) {
|
|
64
|
+
// replace classes
|
|
65
|
+
if (
|
|
66
|
+
instruction[0] === 14 &&
|
|
67
|
+
instruction[1] === 0 &&
|
|
68
|
+
instruction[2] &&
|
|
69
|
+
instruction[2].split(' ').find((i) => classes.has(i.trim()))
|
|
70
|
+
) {
|
|
71
|
+
// 14 - css attribute, 0 - class
|
|
72
|
+
instruction[2] = instruction[2]
|
|
73
|
+
.split(' ')
|
|
74
|
+
.map((className) => {
|
|
75
|
+
if (className.trim() && classes.has(className.trim())) {
|
|
76
|
+
return className.trim() + '_' + postfix;
|
|
77
|
+
} else {
|
|
78
|
+
return className;
|
|
79
|
+
}
|
|
80
|
+
})
|
|
81
|
+
.join(' ');
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// add postfix to tags
|
|
85
|
+
if (
|
|
86
|
+
instruction[0] === 10 &&
|
|
87
|
+
tags.has(inflateTagName(instruction[1]))
|
|
88
|
+
) {
|
|
89
|
+
// 10 - open element
|
|
90
|
+
let existingClassInstruction;
|
|
91
|
+
for (
|
|
92
|
+
let i = opcodes[0].indexOf(instruction);
|
|
93
|
+
i <= opcodes[0].length;
|
|
94
|
+
i++
|
|
95
|
+
) {
|
|
96
|
+
if (opcodes[0][i][0] === 14 && opcodes[0][i][1] === 0) {
|
|
97
|
+
// 14 - css attribute, 0 - class
|
|
98
|
+
existingClassInstruction = opcodes[0][i];
|
|
99
|
+
break;
|
|
100
|
+
}
|
|
101
|
+
if (opcodes[0][i][0] === 12) {
|
|
102
|
+
// 12 - flush element
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (existingClassInstruction) {
|
|
108
|
+
existingClassInstruction[2] += ' ' + postfix;
|
|
109
|
+
} else {
|
|
110
|
+
const classInstruction = [14, 0, postfix, undefined];
|
|
111
|
+
insertions.push([instruction, classInstruction]);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// insert new instructions
|
|
117
|
+
for (let [instruction, classInstruction] of insertions) {
|
|
118
|
+
const index = opcodes[0].indexOf(instruction);
|
|
119
|
+
opcodes[0].splice(index + 1, 0, classInstruction);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// rewrite opcodes
|
|
123
|
+
// const dbg = new WireFormatDebugger(opcodes);
|
|
124
|
+
// const wfd = dbg.format(opcodes);
|
|
125
|
+
|
|
126
|
+
return opcodes;
|
|
127
|
+
});
|
|
128
|
+
},
|
|
129
|
+
};
|
|
130
|
+
});
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// const { RawSource } = require('webpack-sources');
|
|
2
|
+
const rewriteCss = require('./rewriteCss');
|
|
3
|
+
const { readFile, writeFile } = require('fs').promises;
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const getPostfix = require('./getPostfix');
|
|
6
|
+
const fsExists = require('./fsExists');
|
|
7
|
+
const getFiles = require('./getFiles');
|
|
8
|
+
|
|
9
|
+
module.exports = class {
|
|
10
|
+
apply(compiler) {
|
|
11
|
+
if (process.env.EMBER_ENV === 'production') {
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
compiler.hooks.emit.tapAsync(
|
|
16
|
+
'scoped-webpack-plugin',
|
|
17
|
+
async (compilation, callback) => {
|
|
18
|
+
try {
|
|
19
|
+
const cssFiles = await getFiles(
|
|
20
|
+
path.resolve(compiler.context, '**/*.css')
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
// Rewrite the CSS files
|
|
24
|
+
const rewrittenFiles = [];
|
|
25
|
+
// const rewritenFiles = cssFiles.map((file) => {
|
|
26
|
+
for (let file of cssFiles) {
|
|
27
|
+
if (file.endsWith(`/${path.basename(compiler.context)}.css`)) {
|
|
28
|
+
// import scoped.css into app.css
|
|
29
|
+
let appCss = await readFile(file, 'utf-8');
|
|
30
|
+
await writeFile(file, `@import "scoped.css";\n${appCss}`);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (
|
|
34
|
+
[
|
|
35
|
+
path.basename(compiler.context) + '.css',
|
|
36
|
+
'test-support.css',
|
|
37
|
+
].some((f) => file.endsWith(f)) ||
|
|
38
|
+
file.includes('/vendor/') ||
|
|
39
|
+
!(
|
|
40
|
+
(await fsExists(file.replace('.css', '.js'))) ||
|
|
41
|
+
(await fsExists(file.replace('.css', '.hbs')))
|
|
42
|
+
)
|
|
43
|
+
) {
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
const fileName = path.basename(file);
|
|
47
|
+
const postfix = getPostfix(fileName);
|
|
48
|
+
const css = await readFile(file, 'utf-8');
|
|
49
|
+
const rewrittenCss = rewriteCss(css, postfix, fileName);
|
|
50
|
+
|
|
51
|
+
rewrittenFiles.push(rewrittenCss);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Concatenate the rewritten CSS files
|
|
55
|
+
let concatenatedCSS = rewrittenFiles.join('\n\n');
|
|
56
|
+
|
|
57
|
+
// Store the concatenated CSS in the assets/scoped-components.css
|
|
58
|
+
compilation.assets['assets/scoped.css'] = {
|
|
59
|
+
source: () => concatenatedCSS,
|
|
60
|
+
size: () => concatenatedCSS.length,
|
|
61
|
+
};
|
|
62
|
+
} finally {
|
|
63
|
+
callback();
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
const recast = require('recast');
|
|
2
|
+
|
|
3
|
+
module.exports = function (script, removeCallExpression = false) {
|
|
4
|
+
const ast = typeof script === 'string' ? recast.parse(script) : script;
|
|
5
|
+
let css = '';
|
|
6
|
+
|
|
7
|
+
recast.visit(ast, {
|
|
8
|
+
visitCallExpression(nodePath) {
|
|
9
|
+
const node = nodePath.node;
|
|
10
|
+
|
|
11
|
+
if (
|
|
12
|
+
node.callee.name === '__GLIMMER_STYLES' &&
|
|
13
|
+
node.arguments.length === 1
|
|
14
|
+
) {
|
|
15
|
+
css = node.arguments[0].quasis[0].value.raw;
|
|
16
|
+
if (removeCallExpression) {
|
|
17
|
+
nodePath.prune();
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
this.traverse(nodePath);
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
return { css, ast };
|
|
27
|
+
};
|
package/src/fsExists.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
const postcss = require('postcss');
|
|
2
|
+
const parser = require('postcss-selector-parser');
|
|
3
|
+
const isInsideGlobal = require('./isInsideGlobal');
|
|
4
|
+
|
|
5
|
+
function getClassesAndTags(sel, classes, tags) {
|
|
6
|
+
const transform = (sls) => {
|
|
7
|
+
sls.walk((selector) => {
|
|
8
|
+
if (selector.type === 'class' && !isInsideGlobal(selector)) {
|
|
9
|
+
classes.add(selector.value);
|
|
10
|
+
} else if (selector.type === 'tag' && !isInsideGlobal(selector)) {
|
|
11
|
+
tags.add(selector.value);
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
parser(transform).processSync(sel);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
module.exports = function getClassesTagsFromCss(css) {
|
|
20
|
+
const classes = new Set();
|
|
21
|
+
const tags = new Set();
|
|
22
|
+
|
|
23
|
+
const ast = postcss.parse(css);
|
|
24
|
+
ast.walk((node) => {
|
|
25
|
+
if (node.type === 'rule') {
|
|
26
|
+
getClassesAndTags(node.selector, classes, tags);
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
return { classes, tags };
|
|
31
|
+
};
|
package/src/getFiles.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
module.exports = function (css) {
|
|
2
|
+
const regex = /@import\s+["']?([^"')]+)["']?;/g;
|
|
3
|
+
const importedCssPaths = [];
|
|
4
|
+
let match;
|
|
5
|
+
|
|
6
|
+
while ((match = regex.exec(css))) {
|
|
7
|
+
const importPath = match[1];
|
|
8
|
+
if (!importPath.includes('http') && !importPath.startsWith('url(')) {
|
|
9
|
+
css = css.replace(match[0], '');
|
|
10
|
+
importedCssPaths.unshift(importPath);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return {
|
|
15
|
+
css,
|
|
16
|
+
importedCssPaths,
|
|
17
|
+
};
|
|
18
|
+
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
const recast = require('recast');
|
|
2
|
+
const babelParser = require('recast/parsers/babel');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const { readFile } = require('fs').promises;
|
|
5
|
+
const fsExists = require('./fsExists');
|
|
6
|
+
|
|
7
|
+
const parseOptions = {
|
|
8
|
+
parser: babelParser,
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
module.exports = async function (script, id, replaceFunction) {
|
|
12
|
+
const ast = recast.parse(script, parseOptions);
|
|
13
|
+
const cssPath = id.replace(/(\.js)|(\.hbs)/, '.css');
|
|
14
|
+
let css;
|
|
15
|
+
|
|
16
|
+
const cssExists = await fsExists(cssPath);
|
|
17
|
+
if (cssExists) {
|
|
18
|
+
css = await readFile(cssPath, 'utf-8');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (!css) {
|
|
22
|
+
return script;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
recast.visit(ast, {
|
|
26
|
+
visitCallExpression(nodePath) {
|
|
27
|
+
const node = nodePath.node;
|
|
28
|
+
if (
|
|
29
|
+
node.callee.name === 'createTemplateFactory' &&
|
|
30
|
+
node.arguments.length === 1
|
|
31
|
+
) {
|
|
32
|
+
const blockProp = node.arguments[0].properties.find(
|
|
33
|
+
(prop) => prop.key.value === 'block'
|
|
34
|
+
);
|
|
35
|
+
const opcodes = JSON.parse(blockProp.value.value);
|
|
36
|
+
const newOpcodes = replaceFunction(opcodes, css);
|
|
37
|
+
blockProp.value.value = JSON.stringify(newOpcodes);
|
|
38
|
+
|
|
39
|
+
const fileName = path.basename(cssPath);
|
|
40
|
+
// if (!importPath) {
|
|
41
|
+
// unplugin.addWatchFile(cssPath);
|
|
42
|
+
// }
|
|
43
|
+
const importCss = recast.parse(
|
|
44
|
+
`import './${fileName}';\n`,
|
|
45
|
+
parseOptions
|
|
46
|
+
);
|
|
47
|
+
const importCssNode = importCss.program.body[0];
|
|
48
|
+
ast.program.body.unshift(importCssNode);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
this.traverse(nodePath);
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
const resultScript = recast.print(ast).code;
|
|
55
|
+
return resultScript;
|
|
56
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
const recast = require('recast');
|
|
2
|
+
const babelParser = require('recast/parsers/babel');
|
|
3
|
+
|
|
4
|
+
const parseOptions = {
|
|
5
|
+
parser: babelParser,
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
module.exports = function (script, replaceFunction) {
|
|
9
|
+
const ast = recast.parse(script, parseOptions);
|
|
10
|
+
recast.visit(ast, {
|
|
11
|
+
visitCallExpression(path) {
|
|
12
|
+
const node = path.node;
|
|
13
|
+
if (
|
|
14
|
+
node.callee.name === '__GLIMMER_TEMPLATE' ||
|
|
15
|
+
node.callee.name === 'precompileTemplate'
|
|
16
|
+
) {
|
|
17
|
+
if (node.arguments[0].type === 'TemplateLiteral') {
|
|
18
|
+
node.arguments[0].quasis[0].value.raw = replaceFunction(
|
|
19
|
+
node.arguments[0].quasis[0].value.raw
|
|
20
|
+
);
|
|
21
|
+
} else if (
|
|
22
|
+
node.arguments[0].type === 'StringLiteral' ||
|
|
23
|
+
node.arguments[0].type === 'Literal'
|
|
24
|
+
) {
|
|
25
|
+
node.arguments[0].value = replaceFunction(node.arguments[0].value);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
this.traverse(path);
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
return recast.print(ast).code;
|
|
32
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
const parser = require('postcss-selector-parser');
|
|
2
|
+
const postcss = require('postcss');
|
|
3
|
+
const isInsideGlobal = require('./isInsideGlobal');
|
|
4
|
+
|
|
5
|
+
function rewriteSelector(sel, postfix) {
|
|
6
|
+
const transform = (selectors) => {
|
|
7
|
+
selectors.walk((selector) => {
|
|
8
|
+
if (selector.type === 'class' && !isInsideGlobal(selector)) {
|
|
9
|
+
selector.value += '_' + postfix;
|
|
10
|
+
} else if (selector.type === 'tag' && !isInsideGlobal(selector)) {
|
|
11
|
+
selector.replaceWith(
|
|
12
|
+
parser.tag({ value: selector.value }),
|
|
13
|
+
parser.className({ value: postfix })
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
// remove :global
|
|
19
|
+
selectors.walk((selector) => {
|
|
20
|
+
if (selector.type === 'pseudo' && selector.value === ':global') {
|
|
21
|
+
selector.replaceWith(...selector.nodes);
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
};
|
|
25
|
+
const transformed = parser(transform).processSync(sel);
|
|
26
|
+
return transformed;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
module.exports = function rewriteCss(css, postfix, fileName) {
|
|
30
|
+
const ast = postcss.parse(css);
|
|
31
|
+
ast.walk((node) => {
|
|
32
|
+
if (node.type === 'rule') {
|
|
33
|
+
node.selector = rewriteSelector(node.selector, postfix);
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const rewrittenCss = ast.toString();
|
|
38
|
+
return `/* ${fileName} */\n@layer components {\n\n` + rewrittenCss + '\n}\n';
|
|
39
|
+
// return `/* ${fileName} */\n ${rewrittenCss}`;
|
|
40
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
const recast = require('ember-template-recast');
|
|
2
|
+
|
|
3
|
+
module.exports = function rewriteHbs(hbs, classes, tags, postfix) {
|
|
4
|
+
let ast = recast.parse(hbs);
|
|
5
|
+
|
|
6
|
+
recast.traverse(ast, {
|
|
7
|
+
AttrNode(node) {
|
|
8
|
+
if (node.name === 'class' && node.value.chars) {
|
|
9
|
+
const newClasses = node.value.chars.split(' ').map((c) => {
|
|
10
|
+
if (c.trim() && classes.has(c.trim())) {
|
|
11
|
+
return c.trim() + '_' + postfix;
|
|
12
|
+
} else {
|
|
13
|
+
return c;
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
node.value.chars = newClasses.join(' ');
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
ElementNode(node) {
|
|
22
|
+
if (tags.has(node.tag)) {
|
|
23
|
+
// check if class attribute already exists
|
|
24
|
+
const classAttr = node.attributes.find((attr) => attr.name === 'class');
|
|
25
|
+
if (classAttr) {
|
|
26
|
+
classAttr.value.chars += ' ' + postfix;
|
|
27
|
+
} else {
|
|
28
|
+
// push class attribute
|
|
29
|
+
node.attributes.push(
|
|
30
|
+
recast.builders.attr('class', recast.builders.text(postfix))
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
let result = recast.print(ast);
|
|
38
|
+
return result;
|
|
39
|
+
};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
const fs = require('fs/promises');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const {
|
|
4
|
+
preprocessEmbeddedTemplates,
|
|
5
|
+
} = require('ember-template-imports/lib/preprocess-embedded-templates.js');
|
|
6
|
+
const {
|
|
7
|
+
TEMPLATE_TAG_NAME,
|
|
8
|
+
TEMPLATE_TAG_PLACEHOLDER,
|
|
9
|
+
} = require('ember-template-imports/lib/util.js');
|
|
10
|
+
|
|
11
|
+
module.exports = function firstClassComponentTemplates() {
|
|
12
|
+
return {
|
|
13
|
+
name: 'preprocess-fccts',
|
|
14
|
+
async resolveId(source, importer, options) {
|
|
15
|
+
if (source.endsWith('.hbs')) return;
|
|
16
|
+
|
|
17
|
+
for (let ext of ['', '.gjs', '.gts']) {
|
|
18
|
+
let result = await this.resolve(source + ext, importer, {
|
|
19
|
+
...options,
|
|
20
|
+
skipSelf: true,
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
if (result?.external) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (FCCT_EXTENSION.test(result?.id)) {
|
|
28
|
+
return resolutionFor(result.id);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
|
|
33
|
+
async load(id) {
|
|
34
|
+
let originalId = this.getModuleInfo(id)?.meta?.fccts?.originalId ?? id;
|
|
35
|
+
|
|
36
|
+
if (originalId !== id) {
|
|
37
|
+
this.addWatchFile(originalId);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (FCCT_EXTENSION.test(originalId)) {
|
|
41
|
+
return await preprocessTemplates(originalId);
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const FCCT_EXTENSION = /\.g([jt]s)$/;
|
|
48
|
+
|
|
49
|
+
function resolutionFor(originalId) {
|
|
50
|
+
return {
|
|
51
|
+
id: originalId.replace(FCCT_EXTENSION, '.$1'),
|
|
52
|
+
meta: {
|
|
53
|
+
fccts: { originalId },
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async function preprocessTemplates(id) {
|
|
59
|
+
let ember = (await import('ember-source')).default;
|
|
60
|
+
let contents = await fs.readFile(id, 'utf-8');
|
|
61
|
+
|
|
62
|
+
// This is basically taken directly from `ember-template-imports`
|
|
63
|
+
let result = preprocessEmbeddedTemplates(contents, {
|
|
64
|
+
relativePath: path.relative('.', id),
|
|
65
|
+
|
|
66
|
+
getTemplateLocalsRequirePath: ember.absolutePaths.templateCompiler,
|
|
67
|
+
getTemplateLocalsExportPath: '_GlimmerSyntax.getTemplateLocals',
|
|
68
|
+
|
|
69
|
+
templateTag: TEMPLATE_TAG_NAME,
|
|
70
|
+
templateTagReplacement: TEMPLATE_TAG_PLACEHOLDER,
|
|
71
|
+
|
|
72
|
+
includeSourceMaps: true,
|
|
73
|
+
includeTemplateTokens: true,
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
return result.output;
|
|
77
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
const expect = require('chai').expect;
|
|
2
|
+
const getClassesTagsFromCss = require('../src/getClassesTagsFromCss');
|
|
3
|
+
|
|
4
|
+
describe('rewriteCss', function () {
|
|
5
|
+
it('should return classes and tags that are not in :global', function () {
|
|
6
|
+
const css = '.baz :global(.foo) .bar div :global(p) { color: red; }';
|
|
7
|
+
const { classes, tags } = getClassesTagsFromCss(css);
|
|
8
|
+
|
|
9
|
+
// classes should be baz and bar
|
|
10
|
+
expect(classes.size).to.equal(2);
|
|
11
|
+
expect([...classes]).to.have.members(['baz', 'bar']);
|
|
12
|
+
expect(tags.size).to.equal(1);
|
|
13
|
+
expect([...tags]).to.have.members(['div']);
|
|
14
|
+
});
|
|
15
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
const expect = require('chai').expect;
|
|
2
|
+
const { stub } = require('sinon');
|
|
3
|
+
const getPostfix = require('../src/getPostfix');
|
|
4
|
+
|
|
5
|
+
describe('getPostfix', function () {
|
|
6
|
+
it('should return a string', function () {
|
|
7
|
+
const postfix = getPostfix('foo.css');
|
|
8
|
+
|
|
9
|
+
expect(postfix).to.be.a('string');
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it('should return a string starting with "e"', function () {
|
|
13
|
+
const postfix = getPostfix('foo.css');
|
|
14
|
+
|
|
15
|
+
expect(postfix).to.match(/^e/);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('should return a string of length 9', function () {
|
|
19
|
+
const postfix = getPostfix('foo.css');
|
|
20
|
+
|
|
21
|
+
expect(postfix).to.have.lengthOf(9);
|
|
22
|
+
});
|
|
23
|
+
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
const expect = require('chai').expect;
|
|
2
|
+
const { stub } = require('sinon');
|
|
3
|
+
const rewriteCss = require('../src/rewriteCss');
|
|
4
|
+
|
|
5
|
+
describe('rewriteCss', function () {
|
|
6
|
+
it('should rewrite css', function () {
|
|
7
|
+
const css = '.foo { color: red; }';
|
|
8
|
+
const postfix = 'postfix';
|
|
9
|
+
const fileName = 'foo.css';
|
|
10
|
+
const rewritten = rewriteCss(css, postfix, fileName);
|
|
11
|
+
|
|
12
|
+
expect(rewritten).to.equal(
|
|
13
|
+
`/* foo.css */\n@layer components {\n\n.foo_postfix { color: red; }\n}\n`
|
|
14
|
+
);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('shouldnt rewrite global', function () {
|
|
18
|
+
const css = '.baz :global(.foo p) .bar { color: red; }';
|
|
19
|
+
const postfix = 'postfix';
|
|
20
|
+
const fileName = 'foo.css';
|
|
21
|
+
const rewritten = rewriteCss(css, postfix, fileName);
|
|
22
|
+
|
|
23
|
+
expect(rewritten).to.equal(
|
|
24
|
+
`/* foo.css */\n@layer components {\n\n.baz_postfix .foo p .bar_postfix { color: red; }\n}\n`
|
|
25
|
+
);
|
|
26
|
+
});
|
|
27
|
+
});
|
package/README.md
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
# ember-scoped-css
|
|
2
|
-
|
|
3
|
-
[Short description of the addon.]
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
## Compatibility
|
|
7
|
-
|
|
8
|
-
* Ember.js v3.28 or above
|
|
9
|
-
* Ember CLI v3.28 or above
|
|
10
|
-
* Node.js v14 or above
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
## Installation
|
|
14
|
-
|
|
15
|
-
```
|
|
16
|
-
ember install ember-scoped-css
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
## Usage
|
|
21
|
-
|
|
22
|
-
[Longer description of how to use the addon in apps.]
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
## Contributing
|
|
26
|
-
|
|
27
|
-
See the [Contributing](CONTRIBUTING.md) guide for details.
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
## License
|
|
31
|
-
|
|
32
|
-
This project is licensed under the [MIT License](LICENSE.md).
|