ember-scoped-css 0.24.2 → 1.0.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/README.md +110 -115
- package/declarations/lib/path/template-transform-paths.d.ts.map +1 -1
- package/declarations/lib/path/utils.d.ts.map +1 -1
- package/declarations/lib/path/utils.paths.test.d.ts +0 -3
- package/declarations/lib/path/utils.paths.test.d.ts.map +1 -1
- package/dist/cjs/babel-plugin.cjs +2 -72
- package/dist/cjs/index.cjs +206 -390
- package/dist/cjs/template-plugin.cjs +201 -120
- package/package.json +5 -18
- package/src/build/babel-plugin.js +2 -38
- package/src/build/index.js +4 -0
- package/src/build/scoped-css-unplugin.js +31 -193
- package/src/build/template-plugin.js +124 -9
- package/src/lib/{rewriteCss.js → css/rewrite.js} +2 -2
- package/src/lib/css/utils.js +74 -3
- package/src/lib/path/hash-from-absolute-path.test.ts +2 -24
- package/src/lib/path/template-transform-paths.js +0 -15
- package/src/lib/path/template-transform-paths.test.ts +8 -69
- package/src/lib/path/utils.appPath.test.ts +4 -15
- package/src/lib/path/utils.findWorkspacePath.test.ts +16 -22
- package/src/lib/path/utils.hashFrom.test.ts +8 -8
- package/src/lib/path/utils.isRelevantFile.test.ts +9 -29
- package/src/lib/path/utils.js +2 -79
- package/src/lib/path/utils.paths.test.ts +1 -4
- package/src/lib/request.js +40 -0
- package/src/lib/rewriteHbs.js +1 -1
- package/dist/cjs/app-css-loader.cjs +0 -531
- package/dist/cjs/ember-classic-support.cjs +0 -708
- package/src/build/app-css-livereload-loader.js +0 -119
- package/src/build/app-css-loader.js +0 -38
- package/src/build/ember-classic-support.js +0 -293
- package/src/lib/findCssInJs.js +0 -29
- package/src/lib/getClassesTagsFromCss.js +0 -48
- package/src/lib/getImportedCssFiles.js +0 -19
- package/src/lib/isInsideGlobal.js +0 -8
- package/src/lib/replaceGlimmerAst.js +0 -63
- package/src/lib/replaceHbsInJs.js +0 -113
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
import { readFile } from 'node:fs/promises';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
|
|
4
|
-
import { createUnplugin } from 'unplugin';
|
|
5
|
-
import { Compilation } from 'webpack';
|
|
6
|
-
|
|
7
|
-
import generateHash from '../lib/generateAbsolutePathHash.js';
|
|
8
|
-
|
|
9
|
-
export default createUnplugin(({ loaders, htmlEntrypointInfo }) => {
|
|
10
|
-
return {
|
|
11
|
-
name: 'app-css-livereload-loader',
|
|
12
|
-
|
|
13
|
-
transformInclude(id) {
|
|
14
|
-
return id.endsWith('.js') || id.endsWith('.hbs');
|
|
15
|
-
},
|
|
16
|
-
|
|
17
|
-
async transform(code, jsPath) {
|
|
18
|
-
if (process.env.EMBER_ENV === 'production') {
|
|
19
|
-
return code;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const importRegex = /import\s+['"]([^'"]+\.css)['"]\s*;$/gm;
|
|
23
|
-
let cssPaths = [];
|
|
24
|
-
let match;
|
|
25
|
-
|
|
26
|
-
while ((match = importRegex.exec(code))) {
|
|
27
|
-
const importPath = match[1];
|
|
28
|
-
const directory = path.dirname(jsPath);
|
|
29
|
-
const cssPath = path.resolve(directory, importPath);
|
|
30
|
-
|
|
31
|
-
cssPaths.push(cssPath);
|
|
32
|
-
|
|
33
|
-
// replace import with empty string
|
|
34
|
-
code = code.replace(match[0], '');
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
if (!cssPaths.length) {
|
|
38
|
-
return code;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const promises = cssPaths.map(async (cssPath) => {
|
|
42
|
-
let css = await readFile(cssPath, 'utf8');
|
|
43
|
-
|
|
44
|
-
for (let i = loaders.length - 1; i >= 0; i--) {
|
|
45
|
-
const loader = loaders[i];
|
|
46
|
-
|
|
47
|
-
css = await loader.bind({ resourcePath: cssPath })(css);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// random string; lenght is 8
|
|
51
|
-
const postfix = generateHash(path.basename(cssPath));
|
|
52
|
-
|
|
53
|
-
this.emitFile({
|
|
54
|
-
type: 'asset',
|
|
55
|
-
fileName:
|
|
56
|
-
'assets/includedscripts/' + postfix + '_' + path.basename(cssPath),
|
|
57
|
-
source: css,
|
|
58
|
-
});
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
await Promise.all(promises);
|
|
62
|
-
|
|
63
|
-
return code;
|
|
64
|
-
},
|
|
65
|
-
|
|
66
|
-
webpack(compiler) {
|
|
67
|
-
if (process.env.EMBER_ENV === 'production') {
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
compiler.hooks.thisCompilation.tap('Replace', (compilation) => {
|
|
72
|
-
compilation.hooks.processAssets.tapAsync(
|
|
73
|
-
{
|
|
74
|
-
name: 'Replace',
|
|
75
|
-
stage: Compilation.PROCESS_ASSETS_STAGE_DERIVED,
|
|
76
|
-
},
|
|
77
|
-
async (assets, callback) => {
|
|
78
|
-
try {
|
|
79
|
-
const cssAssets = Object.keys(assets).filter((asset) =>
|
|
80
|
-
asset.startsWith('assets/includedscripts/'),
|
|
81
|
-
);
|
|
82
|
-
|
|
83
|
-
if (!cssAssets.length) {
|
|
84
|
-
return;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// let linkAdded = false;
|
|
88
|
-
const document =
|
|
89
|
-
htmlEntrypointInfo.htmlEntryPoint.dom.window.document;
|
|
90
|
-
|
|
91
|
-
for (let asset of cssAssets) {
|
|
92
|
-
const head = document.getElementsByTagName('head')[0];
|
|
93
|
-
const linkExists = head.querySelector(
|
|
94
|
-
`link[rel="stylesheet"][href="/${asset}"]`,
|
|
95
|
-
);
|
|
96
|
-
|
|
97
|
-
if (!linkExists) {
|
|
98
|
-
const link = document.createElement('link');
|
|
99
|
-
|
|
100
|
-
link.rel = 'stylesheet';
|
|
101
|
-
link.href = '/' + asset;
|
|
102
|
-
head.appendChild(link);
|
|
103
|
-
// linkAdded = true;
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// if (linkAdded) {
|
|
108
|
-
// const indexHtmlWithLinks = dom.serialize();
|
|
109
|
-
// await writeFile(indexHtmlPath, indexHtmlWithLinks, 'utf8');
|
|
110
|
-
// }
|
|
111
|
-
} finally {
|
|
112
|
-
callback();
|
|
113
|
-
}
|
|
114
|
-
},
|
|
115
|
-
);
|
|
116
|
-
});
|
|
117
|
-
},
|
|
118
|
-
};
|
|
119
|
-
});
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
// import { createUnplugin } from 'unplugin';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
|
|
4
|
-
import {
|
|
5
|
-
cssHasAssociatedComponent,
|
|
6
|
-
hashFrom,
|
|
7
|
-
isRelevantFile,
|
|
8
|
-
} from '../lib/path/utils.js';
|
|
9
|
-
import rewriteCss from '../lib/rewriteCss.js';
|
|
10
|
-
|
|
11
|
-
export default async function (code) {
|
|
12
|
-
const options = this.getOptions();
|
|
13
|
-
const cssPath = this.resourcePath;
|
|
14
|
-
|
|
15
|
-
if (!isRelevantFile(cssPath, { ...options, cwd: this.rootContext })) {
|
|
16
|
-
return code;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
if (!cssPath.startsWith(this.rootContext)) {
|
|
20
|
-
return code;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const cssFileName = path.basename(cssPath);
|
|
24
|
-
|
|
25
|
-
if (cssHasAssociatedComponent(cssPath)) {
|
|
26
|
-
const postfix = hashFrom(cssPath);
|
|
27
|
-
const rewrittenCss = rewriteCss(
|
|
28
|
-
code,
|
|
29
|
-
postfix,
|
|
30
|
-
cssFileName,
|
|
31
|
-
options.layerName,
|
|
32
|
-
);
|
|
33
|
-
|
|
34
|
-
return rewrittenCss;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
return code;
|
|
38
|
-
}
|
|
@@ -1,293 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
import { existsSync } from 'node:fs';
|
|
4
|
-
import { readFile, writeFile } from 'node:fs/promises';
|
|
5
|
-
import path from 'node:path';
|
|
6
|
-
|
|
7
|
-
import Concat from 'broccoli-concat';
|
|
8
|
-
import { Funnel } from 'broccoli-funnel';
|
|
9
|
-
import MergeTrees from 'broccoli-merge-trees';
|
|
10
|
-
import Filter from 'broccoli-persistent-filter';
|
|
11
|
-
import { Preprocessor } from 'content-tag';
|
|
12
|
-
|
|
13
|
-
import getClassesTagsFromCss from '../lib/getClassesTagsFromCss.js';
|
|
14
|
-
import {
|
|
15
|
-
cssHasAssociatedComponent,
|
|
16
|
-
hashFromModulePath,
|
|
17
|
-
packageScopedPathToModulePath,
|
|
18
|
-
} from '../lib/path/utils.js';
|
|
19
|
-
import rewriteCss from '../lib/rewriteCss.js';
|
|
20
|
-
import rewriteHbs from '../lib/rewriteHbs.js';
|
|
21
|
-
|
|
22
|
-
const p = new Preprocessor();
|
|
23
|
-
const COMPONENT_EXTENSIONS = ['hbs', 'js', 'ts', 'gjs', 'gts'];
|
|
24
|
-
const TEMPLATE_EXTENSIONS = ['hbs', 'gjs', 'gts'];
|
|
25
|
-
|
|
26
|
-
class ScopedFilter extends Filter {
|
|
27
|
-
constructor(componentsNode, options = {}) {
|
|
28
|
-
super(componentsNode, options);
|
|
29
|
-
this.warningStream = process.stderr;
|
|
30
|
-
this.options = options;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
get extensions() {
|
|
34
|
-
return ['css'];
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
get targetExtension() {
|
|
38
|
-
return 'css';
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
async processString(content, relativePath) {
|
|
42
|
-
// ignore css files for ember-css-modules
|
|
43
|
-
if (relativePath.endsWith('.module.css')) {
|
|
44
|
-
return '';
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// check if corresponding js file exists
|
|
48
|
-
let hasRelevantFile = false;
|
|
49
|
-
|
|
50
|
-
for (let inputPath of this.inputPaths) {
|
|
51
|
-
if (hasRelevantFile) break;
|
|
52
|
-
|
|
53
|
-
for (let ext of COMPONENT_EXTENSIONS) {
|
|
54
|
-
if (hasRelevantFile) break;
|
|
55
|
-
|
|
56
|
-
const relativeComponentPath = relativePath.replace(/\.css$/, '.' + ext);
|
|
57
|
-
const componentPath = path.join(inputPath, relativeComponentPath);
|
|
58
|
-
|
|
59
|
-
if (existsSync(componentPath)) {
|
|
60
|
-
hasRelevantFile = true;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Pods support
|
|
66
|
-
*/
|
|
67
|
-
if (relativePath.endsWith('styles.css')) {
|
|
68
|
-
const absoluteCssPath = path.join(inputPath, relativePath);
|
|
69
|
-
|
|
70
|
-
if (cssHasAssociatedComponent(absoluteCssPath)) {
|
|
71
|
-
hasRelevantFile = true;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// rewrite css file
|
|
77
|
-
if (hasRelevantFile) {
|
|
78
|
-
let localPackagerStylePath = packageScopedPathToModulePath(relativePath);
|
|
79
|
-
|
|
80
|
-
const hash = hashFromModulePath(localPackagerStylePath);
|
|
81
|
-
|
|
82
|
-
content = rewriteCss(
|
|
83
|
-
content,
|
|
84
|
-
hash,
|
|
85
|
-
relativePath,
|
|
86
|
-
this.options.getUserOptions?.()?.layerName,
|
|
87
|
-
);
|
|
88
|
-
|
|
89
|
-
return content;
|
|
90
|
-
} else {
|
|
91
|
-
return '';
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
async postProcess(results, relativePath) {
|
|
96
|
-
if (process.env.CI || relativePath.endsWith('.module.css')) {
|
|
97
|
-
return results;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
for (let inputPath of this.inputPaths) {
|
|
101
|
-
const templateFile = {};
|
|
102
|
-
|
|
103
|
-
eachExtension: for (let ext of TEMPLATE_EXTENSIONS) {
|
|
104
|
-
const templatePath = relativePath.replace(/\.css/, '.' + ext);
|
|
105
|
-
let templateFilePath = path.join(inputPath, templatePath);
|
|
106
|
-
let exists = existsSync(templateFilePath);
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Pods support
|
|
110
|
-
*/
|
|
111
|
-
if (ext === 'hbs' && !exists && relativePath.endsWith('styles.css')) {
|
|
112
|
-
let podsName = relativePath.replace(/styles\.css$/, 'template.hbs');
|
|
113
|
-
|
|
114
|
-
templateFilePath = path.join(inputPath, podsName);
|
|
115
|
-
exists = existsSync(templateFilePath);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
if (exists) {
|
|
119
|
-
templateFile.path = templateFilePath;
|
|
120
|
-
templateFile.ext = ext;
|
|
121
|
-
|
|
122
|
-
break eachExtension;
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// if the template exists we check the css for changes
|
|
127
|
-
if (templateFile.path) {
|
|
128
|
-
const cssFilePath = path.join(inputPath, relativePath);
|
|
129
|
-
const cssContents = await readFile(cssFilePath, 'utf-8');
|
|
130
|
-
const { classes, tags } = getClassesTagsFromCss(cssContents);
|
|
131
|
-
const previousClasses = this.options.previousClasses.get(relativePath);
|
|
132
|
-
|
|
133
|
-
// if we have previous classes, and they are different, build templates to compare
|
|
134
|
-
if (previousClasses && classes !== previousClasses) {
|
|
135
|
-
const localPackagerStylePath =
|
|
136
|
-
packageScopedPathToModulePath(relativePath);
|
|
137
|
-
const postfix = hashFromModulePath(localPackagerStylePath);
|
|
138
|
-
const templateRaw = await readFile(templateFile.path, 'utf-8');
|
|
139
|
-
const templateComparison = [];
|
|
140
|
-
let templateContents = templateRaw;
|
|
141
|
-
|
|
142
|
-
if (templateFile.ext === 'hbs') {
|
|
143
|
-
templateComparison.push(
|
|
144
|
-
didTemplateChange(
|
|
145
|
-
templateContents,
|
|
146
|
-
previousClasses,
|
|
147
|
-
classes,
|
|
148
|
-
tags,
|
|
149
|
-
postfix,
|
|
150
|
-
),
|
|
151
|
-
);
|
|
152
|
-
} else {
|
|
153
|
-
// find all template tags, and extract the contents to compare
|
|
154
|
-
const results = p.parse(templateRaw, '');
|
|
155
|
-
const templates = results.map((x) => x.contents);
|
|
156
|
-
|
|
157
|
-
for (let template of templates) {
|
|
158
|
-
templateComparison.push(
|
|
159
|
-
didTemplateChange(
|
|
160
|
-
template,
|
|
161
|
-
previousClasses,
|
|
162
|
-
classes,
|
|
163
|
-
tags,
|
|
164
|
-
postfix,
|
|
165
|
-
),
|
|
166
|
-
);
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
const templateChanged = templateComparison.some((v) => v);
|
|
171
|
-
|
|
172
|
-
// if the rewrite doesn't match the original output, we want to rebuild the template
|
|
173
|
-
if (templateChanged) {
|
|
174
|
-
// this is an awful hack because we don't know how to invalidate broccoli-persistent-filter cache
|
|
175
|
-
// ideally we'd invalidate the broccoli-peristent-filter cache here
|
|
176
|
-
await writeFile(templateFile.path, templateContents + ' ');
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
// assign for next run comparison
|
|
180
|
-
|
|
181
|
-
this.options.previousClasses.set(relativePath, classes);
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
return results;
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
/**
|
|
190
|
-
* Compares a template with the context of different sets of extraced css classes
|
|
191
|
-
*
|
|
192
|
-
* @param {string} contents
|
|
193
|
-
* @param {Set} previousClasses
|
|
194
|
-
* @param {Set} currentClasses
|
|
195
|
-
* @param {Set} tags
|
|
196
|
-
* @param {string} postfix
|
|
197
|
-
* @returns {Boolean}
|
|
198
|
-
*/
|
|
199
|
-
function didTemplateChange(
|
|
200
|
-
contents,
|
|
201
|
-
previousClasses,
|
|
202
|
-
currentClasses,
|
|
203
|
-
tags,
|
|
204
|
-
postfix,
|
|
205
|
-
) {
|
|
206
|
-
const original = rewriteHbs(contents, previousClasses, tags, postfix);
|
|
207
|
-
const current = rewriteHbs(contents, currentClasses, tags, postfix);
|
|
208
|
-
|
|
209
|
-
return original !== current;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
export default class ScopedCssPreprocessor {
|
|
213
|
-
constructor(options) {
|
|
214
|
-
this.owner = options.owner;
|
|
215
|
-
this.appName = options.owner.parent.pkg.name;
|
|
216
|
-
this.name = 'scoped-css-preprocessor';
|
|
217
|
-
this.previousClasses = new Map();
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
/**
|
|
221
|
-
* Sets the options from `setupPreprocessorRegistry`, the v1-Addon Hook
|
|
222
|
-
* @param {{ layerName?: string }} options
|
|
223
|
-
*/
|
|
224
|
-
configureOptions(options) {
|
|
225
|
-
this.userOptions = options || { ['not configured']: true };
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
toTree(inputNode, inputPath, outputDirectory, options) {
|
|
229
|
-
const otherPreprocessors = this.preprocessors.filter((p) => p !== this);
|
|
230
|
-
const otherTrees = otherPreprocessors.map((p) => p.toTree(...arguments));
|
|
231
|
-
let mergedOtherTrees = new MergeTrees(otherTrees);
|
|
232
|
-
|
|
233
|
-
let roots = [
|
|
234
|
-
this.appName + '/components/',
|
|
235
|
-
...(this.userOptions.additionalRoots || []).map(
|
|
236
|
-
(root) => `${this.appName}/${root}`,
|
|
237
|
-
),
|
|
238
|
-
];
|
|
239
|
-
let include = roots
|
|
240
|
-
.map((root) => {
|
|
241
|
-
let withSlash = root.endsWith('/') ? root : `${root}/`;
|
|
242
|
-
|
|
243
|
-
return [
|
|
244
|
-
`${withSlash}**/*.css`,
|
|
245
|
-
...COMPONENT_EXTENSIONS.map((ext) => `${withSlash}**/*.${ext}`),
|
|
246
|
-
];
|
|
247
|
-
})
|
|
248
|
-
.flat();
|
|
249
|
-
|
|
250
|
-
let componentsNode = new Funnel(inputNode, { include });
|
|
251
|
-
|
|
252
|
-
componentsNode = new ScopedFilter(componentsNode, {
|
|
253
|
-
inputPath,
|
|
254
|
-
getUserOptions: () => this.userOptions,
|
|
255
|
-
owner: this.owner,
|
|
256
|
-
previousClasses: this.previousClasses,
|
|
257
|
-
});
|
|
258
|
-
|
|
259
|
-
const componentStyles = new Funnel(componentsNode, {
|
|
260
|
-
include: roots.map((root) => {
|
|
261
|
-
let withSlash = root.endsWith('/') ? root : `${root}/`;
|
|
262
|
-
|
|
263
|
-
return `${withSlash}**/*.css`;
|
|
264
|
-
}),
|
|
265
|
-
});
|
|
266
|
-
|
|
267
|
-
const appCss = new Funnel(inputNode, {
|
|
268
|
-
include: [this.appName + '/styles/app.css'],
|
|
269
|
-
});
|
|
270
|
-
|
|
271
|
-
let mergedStyles = new MergeTrees(
|
|
272
|
-
[appCss, mergedOtherTrees, componentStyles],
|
|
273
|
-
{ overwrite: true },
|
|
274
|
-
);
|
|
275
|
-
|
|
276
|
-
let newOutput = new Concat(mergedStyles, {
|
|
277
|
-
outputFile: options.outputPaths['app'],
|
|
278
|
-
allowNone: true,
|
|
279
|
-
sourceMapConfig: { enabled: true },
|
|
280
|
-
});
|
|
281
|
-
|
|
282
|
-
if (this.userOptions?.passthrough) {
|
|
283
|
-
let passedThrough = new Funnel(mergedStyles, {
|
|
284
|
-
include: this.userOptions.passthrough,
|
|
285
|
-
destDir: this.userOptions.passthroughDestination,
|
|
286
|
-
});
|
|
287
|
-
|
|
288
|
-
return new MergeTrees([passedThrough, newOutput], { overwrite: true });
|
|
289
|
-
} else {
|
|
290
|
-
return newOutput;
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
}
|
package/src/lib/findCssInJs.js
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import recast from 'recast';
|
|
2
|
-
|
|
3
|
-
export default 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
|
-
|
|
17
|
-
if (removeCallExpression) {
|
|
18
|
-
nodePath.prune();
|
|
19
|
-
|
|
20
|
-
return false;
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
this.traverse(nodePath);
|
|
25
|
-
},
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
return { css, ast };
|
|
29
|
-
}
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import postcss from 'postcss';
|
|
2
|
-
import parser from 'postcss-selector-parser';
|
|
3
|
-
|
|
4
|
-
import isInsideGlobal from './isInsideGlobal.js';
|
|
5
|
-
|
|
6
|
-
function getClassesAndTags(sel, classes, tags) {
|
|
7
|
-
const transform = (sls) => {
|
|
8
|
-
sls.walk((selector) => {
|
|
9
|
-
if (selector.type === 'class' && !isInsideGlobal(selector)) {
|
|
10
|
-
classes.add(selector.value);
|
|
11
|
-
} else if (selector.type === 'tag' && !isInsideGlobal(selector)) {
|
|
12
|
-
tags.add(selector.value);
|
|
13
|
-
}
|
|
14
|
-
});
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
parser(transform).processSync(sel);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export default function getClassesTagsFromCss(css) {
|
|
21
|
-
const classes = new Set();
|
|
22
|
-
const tags = new Set();
|
|
23
|
-
|
|
24
|
-
const ast = postcss.parse(css);
|
|
25
|
-
|
|
26
|
-
ast.walk((node) => {
|
|
27
|
-
if (node.type === 'rule') {
|
|
28
|
-
getClassesAndTags(node.selector, classes, tags);
|
|
29
|
-
}
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
return { classes, tags };
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
if (import.meta.vitest) {
|
|
36
|
-
const { it, expect } = import.meta.vitest;
|
|
37
|
-
|
|
38
|
-
it('should return classes and tags that are not in :global', function () {
|
|
39
|
-
const css = '.baz :global(.foo) .bar div :global(p) { color: red; }';
|
|
40
|
-
const { classes, tags } = getClassesTagsFromCss(css);
|
|
41
|
-
|
|
42
|
-
// classes should be baz and bar
|
|
43
|
-
expect(classes.size).to.equal(2);
|
|
44
|
-
expect([...classes]).to.have.members(['baz', 'bar']);
|
|
45
|
-
expect(tags.size).to.equal(1);
|
|
46
|
-
expect([...tags]).to.have.members(['div']);
|
|
47
|
-
});
|
|
48
|
-
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
export default 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
|
-
|
|
9
|
-
if (!importPath.includes('http') && !importPath.startsWith('url(')) {
|
|
10
|
-
css = css.replace(match[0], '');
|
|
11
|
-
importedCssPaths.unshift(importPath);
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
return {
|
|
16
|
-
css,
|
|
17
|
-
importedCssPaths,
|
|
18
|
-
};
|
|
19
|
-
}
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import { existsSync } from 'node:fs';
|
|
2
|
-
import { readFile } from 'node:fs/promises';
|
|
3
|
-
import path from 'node:path';
|
|
4
|
-
|
|
5
|
-
import babelParser from '@babel/parser';
|
|
6
|
-
import recast from 'recast';
|
|
7
|
-
|
|
8
|
-
const parseOptions = {
|
|
9
|
-
parser: babelParser,
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
export default async function replaceGlimmerAst(script, id, replaceFunction) {
|
|
13
|
-
const ast = recast.parse(script, parseOptions);
|
|
14
|
-
const cssPath = id.replace(/(\.js)|(\.hbs)/, '.css');
|
|
15
|
-
let css;
|
|
16
|
-
|
|
17
|
-
const cssExists = existsSync(cssPath);
|
|
18
|
-
|
|
19
|
-
if (cssExists) {
|
|
20
|
-
css = await readFile(cssPath, 'utf-8');
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
if (!css) {
|
|
24
|
-
return script;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
recast.visit(ast, {
|
|
28
|
-
visitCallExpression(nodePath) {
|
|
29
|
-
const node = nodePath.node;
|
|
30
|
-
|
|
31
|
-
if (
|
|
32
|
-
node.callee.name === 'createTemplateFactory' &&
|
|
33
|
-
node.arguments.length === 1
|
|
34
|
-
) {
|
|
35
|
-
const blockProp = node.arguments[0].properties.find(
|
|
36
|
-
(prop) => prop.key.value === 'block',
|
|
37
|
-
);
|
|
38
|
-
const opcodes = JSON.parse(blockProp.value.value);
|
|
39
|
-
const newOpcodes = replaceFunction(opcodes, css);
|
|
40
|
-
|
|
41
|
-
blockProp.value.value = JSON.stringify(newOpcodes);
|
|
42
|
-
|
|
43
|
-
const fileName = path.basename(cssPath);
|
|
44
|
-
// if (!importPath) {
|
|
45
|
-
// unplugin.addWatchFile(cssPath);
|
|
46
|
-
// }
|
|
47
|
-
const importCss = recast.parse(
|
|
48
|
-
`import './${fileName}';\n`,
|
|
49
|
-
parseOptions,
|
|
50
|
-
);
|
|
51
|
-
const importCssNode = importCss.program.body[0];
|
|
52
|
-
|
|
53
|
-
ast.program.body.unshift(importCssNode);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
this.traverse(nodePath);
|
|
57
|
-
},
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
const resultScript = recast.print(ast).code;
|
|
61
|
-
|
|
62
|
-
return resultScript;
|
|
63
|
-
}
|