auklet 0.2.6 → 0.2.8
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 +7 -5
- package/dist/css/core/styleModuleEntryPlanner.js +1 -1
- package/dist/css/core/stylePackageContext.d.ts +6 -1
- package/dist/css/core/stylePackageContext.js +23 -1
- package/dist/css/core/styleProcessor.d.ts +10 -0
- package/dist/css/core/styleProcessor.js +148 -23
- package/dist/css/core/workspaceStyleResolver.d.ts +5 -0
- package/dist/css/core/workspaceStyleResolver.js +35 -3
- package/dist/css/production/builder.js +1 -0
- package/dist/css/production/format/moduleWriter.d.ts +1 -0
- package/dist/css/production/format/moduleWriter.js +7 -7
- package/dist/css/production/format/sourceWriter.d.ts +1 -1
- package/dist/css/production/format/sourceWriter.js +10 -4
- package/dist/css/production/moduleOutputWriter.js +1 -0
- package/dist/css/vite/moduleGraph/styleCodeFactory.d.ts +5 -0
- package/dist/css/vite/moduleGraph/styleCodeFactory.js +104 -17
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -181,8 +181,10 @@ pattern syntax is a small glob subset: `*`, `**`, and `?`.
|
|
|
181
181
|
|
|
182
182
|
For example, `components/CodeBlock/index.css` may import
|
|
183
183
|
`../../internal/syntaxHighlight.css` when it matches `styles.shared`; component
|
|
184
|
-
CSS outputs
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
184
|
+
CSS outputs keep that `@import` relationship instead of expanding the shared
|
|
185
|
+
rules into every component output. The shared CSS file is still copied as its
|
|
186
|
+
own source-level CSS file. `dist/index.css` remains the full package CSS
|
|
187
|
+
aggregate, while format-level and component-level CSS entries preserve the
|
|
188
|
+
import graph as much as possible. Shared CSS cannot import component CSS or
|
|
189
|
+
theme CSS. Component-to-component CSS imports are still rejected; package CSS
|
|
190
|
+
dependencies should be expressed through `styles.dependencies`.
|
|
@@ -62,7 +62,7 @@ export class StyleModuleEntryPlanner {
|
|
|
62
62
|
throw new Error(`[css] cross-package CSS import detected: ${importer} imports ${imported}. ` +
|
|
63
63
|
'Use styles.dependencies to express package style dependencies.');
|
|
64
64
|
}
|
|
65
|
-
if (this.packageContext.
|
|
65
|
+
if (this.packageContext.shouldAllowSharedStyleImport(item))
|
|
66
66
|
continue;
|
|
67
67
|
if (this.packageContext.isSharedStyleFile(item.importer)) {
|
|
68
68
|
const importer = this.toRelativeSourceFile(item.importer);
|
|
@@ -22,14 +22,19 @@ export declare class StylePackageContext {
|
|
|
22
22
|
private readonly sourceModuleDirs;
|
|
23
23
|
private readonly themeStyleFileKeys;
|
|
24
24
|
private readonly sharedStyleFileKeys;
|
|
25
|
+
private hasValidatedSourceRootLocalStyleImports;
|
|
26
|
+
private hasValidatedPreservedLocalStyleImports;
|
|
25
27
|
private moduleStyleEntryPlanner?;
|
|
26
28
|
private moduleStyleImports?;
|
|
27
29
|
constructor(options: StylePackageContextOptions);
|
|
28
30
|
getStyleFiles(files: Array<string>): string[];
|
|
29
31
|
getModuleStyleImports(): Map<string, string[]>;
|
|
30
32
|
getModuleStyleEntryPlanner(): StyleModuleEntryPlanner;
|
|
33
|
+
getStyleEntryFiles(): string[];
|
|
31
34
|
isSharedStyleFile(file: string): boolean;
|
|
32
|
-
|
|
35
|
+
shouldAllowSharedStyleImport(reference: StyleFileImportReference): boolean;
|
|
36
|
+
assertPreservedLocalStyleImports(): void;
|
|
37
|
+
assertNoSourceRootEscapingLocalStyleImports(): void;
|
|
33
38
|
private isSharedHelperStyleFile;
|
|
34
39
|
private getSourceModuleDirs;
|
|
35
40
|
private isSourceModuleStyleFile;
|
|
@@ -23,6 +23,8 @@ export class StylePackageContext {
|
|
|
23
23
|
sourceModuleDirs;
|
|
24
24
|
themeStyleFileKeys;
|
|
25
25
|
sharedStyleFileKeys;
|
|
26
|
+
hasValidatedSourceRootLocalStyleImports = false;
|
|
27
|
+
hasValidatedPreservedLocalStyleImports = false;
|
|
26
28
|
moduleStyleEntryPlanner;
|
|
27
29
|
moduleStyleImports;
|
|
28
30
|
constructor(options) {
|
|
@@ -61,15 +63,35 @@ export class StylePackageContext {
|
|
|
61
63
|
this.moduleStyleEntryPlanner ??= new StyleModuleEntryPlanner(this);
|
|
62
64
|
return this.moduleStyleEntryPlanner;
|
|
63
65
|
}
|
|
66
|
+
getStyleEntryFiles() {
|
|
67
|
+
const importedStyleFiles = this.styleProcessor.collectImportedStyleFiles(this.styleFiles);
|
|
68
|
+
return this.styleFiles.filter((styleFile) => !importedStyleFiles.has(path.resolve(styleFile)));
|
|
69
|
+
}
|
|
64
70
|
isSharedStyleFile(file) {
|
|
65
71
|
return this.sharedStyleFileKeys.has(normalizeFileKey(file));
|
|
66
72
|
}
|
|
67
|
-
|
|
73
|
+
shouldAllowSharedStyleImport(reference) {
|
|
68
74
|
return ((this.isSharedStyleFile(reference.imported) &&
|
|
69
75
|
this.isSharedHelperStyleFile(reference.imported)) ||
|
|
70
76
|
(this.isSharedStyleFile(reference.importer) &&
|
|
71
77
|
this.isSharedHelperStyleFile(reference.imported)));
|
|
72
78
|
}
|
|
79
|
+
assertPreservedLocalStyleImports() {
|
|
80
|
+
if (this.hasValidatedPreservedLocalStyleImports)
|
|
81
|
+
return;
|
|
82
|
+
this.assertNoSourceRootEscapingLocalStyleImports();
|
|
83
|
+
this.styleProcessor.assertNoLocalStyleImportCycles(this.styleFiles);
|
|
84
|
+
this.hasValidatedPreservedLocalStyleImports = true;
|
|
85
|
+
}
|
|
86
|
+
assertNoSourceRootEscapingLocalStyleImports() {
|
|
87
|
+
if (this.hasValidatedSourceRootLocalStyleImports)
|
|
88
|
+
return;
|
|
89
|
+
this.styleProcessor.assertNoSourceRootEscapingLocalStyleImports([
|
|
90
|
+
...this.styleFiles,
|
|
91
|
+
...this.themeFiles.values(),
|
|
92
|
+
]);
|
|
93
|
+
this.hasValidatedSourceRootLocalStyleImports = true;
|
|
94
|
+
}
|
|
73
95
|
isSharedHelperStyleFile(file) {
|
|
74
96
|
return (this.isInsideSourceRoot(file) &&
|
|
75
97
|
!this.isThemeStyleFile(file) &&
|
|
@@ -4,8 +4,10 @@ import type { WorkspaceStyleResolver } from '#auklet/css/core/workspaceStyleReso
|
|
|
4
4
|
export type StyleFileImportReference = {
|
|
5
5
|
importer: string;
|
|
6
6
|
imported: string;
|
|
7
|
+
specifier: string;
|
|
7
8
|
};
|
|
8
9
|
export type StyleFileImportExpandOptions = {
|
|
10
|
+
mapImportSpecifier?: (reference: StyleFileImportReference) => string;
|
|
9
11
|
shouldExpandImport?: (reference: StyleFileImportReference) => boolean;
|
|
10
12
|
};
|
|
11
13
|
export declare class StyleProcessor {
|
|
@@ -17,9 +19,17 @@ export declare class StyleProcessor {
|
|
|
17
19
|
stringify(root: Root): string;
|
|
18
20
|
appendStyleContent(target: Root, content: string, from: string): void;
|
|
19
21
|
readStyleFile(stylePath: string, seen?: Set<string>, options?: StyleFileImportExpandOptions): string;
|
|
22
|
+
private isSourceStyleImportSpecifier;
|
|
23
|
+
private throwMissingLocalStyleImport;
|
|
20
24
|
collectImportedStyleFiles(styleFiles: Array<string>): Set<string>;
|
|
21
25
|
collectImportedStyleFileReferences(styleFiles: Array<string>): StyleFileImportReference[];
|
|
22
26
|
collectStyleImportSpecifiers(styleFiles: Array<string>): Set<string>;
|
|
27
|
+
assertNoLocalStyleImportCycles(styleFiles: Array<string>): void;
|
|
28
|
+
assertNoSourceRootEscapingLocalStyleImports(styleFiles: Array<string>): void;
|
|
23
29
|
private parse;
|
|
24
30
|
private parseImportSpecifier;
|
|
31
|
+
private resolveStyleImportReference;
|
|
32
|
+
private assertSourceRootLocalStyleImport;
|
|
33
|
+
private collectLocalStyleImportReferences;
|
|
34
|
+
private parseImportSpecifierReference;
|
|
25
35
|
}
|
|
@@ -44,29 +44,39 @@ export class StyleProcessor {
|
|
|
44
44
|
const root = this.parse(css, stylePath);
|
|
45
45
|
const imports = [];
|
|
46
46
|
root.walkAtRules('import', (rule) => {
|
|
47
|
-
const
|
|
48
|
-
if (
|
|
49
|
-
imports.push({ rule,
|
|
47
|
+
const reference = this.parseImportSpecifierReference(rule.params);
|
|
48
|
+
if (reference)
|
|
49
|
+
imports.push({ rule, ...reference });
|
|
50
50
|
});
|
|
51
|
-
for (const { rule, specifier } of imports) {
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
if (options.shouldExpandImport?.(reference) === false)
|
|
51
|
+
for (const { rule, specifier, specifierStart, specifierEnd } of imports) {
|
|
52
|
+
const { reference, isLocalStyleImport } = this.resolveStyleImportReference(specifier, stylePath);
|
|
53
|
+
this.assertSourceRootLocalStyleImport(reference, isLocalStyleImport);
|
|
54
|
+
const mappedSpecifier = options.mapImportSpecifier?.(reference);
|
|
55
|
+
if (mappedSpecifier) {
|
|
56
|
+
rule.params = `${rule.params.slice(0, specifierStart)}${mappedSpecifier}${rule.params.slice(specifierEnd)}`;
|
|
57
|
+
}
|
|
58
|
+
if (options.shouldExpandImport?.(reference) === false) {
|
|
60
59
|
continue;
|
|
61
|
-
|
|
60
|
+
}
|
|
61
|
+
const content = this.readStyleFile(reference.imported, seen, options);
|
|
62
62
|
if (!content.trim()) {
|
|
63
63
|
rule.remove();
|
|
64
64
|
continue;
|
|
65
65
|
}
|
|
66
|
-
rule.replaceWith(...(this.parse(content,
|
|
66
|
+
rule.replaceWith(...(this.parse(content, reference.imported).nodes ?? []));
|
|
67
67
|
}
|
|
68
68
|
return root.toString();
|
|
69
69
|
}
|
|
70
|
+
isSourceStyleImportSpecifier(specifier, importedPath) {
|
|
71
|
+
if (specifier.startsWith('#'))
|
|
72
|
+
return true;
|
|
73
|
+
return (this.config.styleExtensions.includes(path.extname(importedPath ?? specifier)) &&
|
|
74
|
+
(specifier.startsWith('.') ||
|
|
75
|
+
(importedPath ? this.resolver.isInsideSourceRoot(importedPath) : false)));
|
|
76
|
+
}
|
|
77
|
+
throwMissingLocalStyleImport(specifier, stylePath) {
|
|
78
|
+
throw new Error(`[css] local CSS import not found: ${specifier} from ${stylePath}`);
|
|
79
|
+
}
|
|
70
80
|
collectImportedStyleFiles(styleFiles) {
|
|
71
81
|
return new Set(this.collectImportedStyleFileReferences(styleFiles).map((item) => item.imported));
|
|
72
82
|
}
|
|
@@ -77,14 +87,13 @@ export class StyleProcessor {
|
|
|
77
87
|
const root = this.parse(css, styleFile);
|
|
78
88
|
root.walkAtRules('import', (rule) => {
|
|
79
89
|
const specifier = this.parseImportSpecifier(rule.params);
|
|
80
|
-
if (!specifier
|
|
81
|
-
|
|
90
|
+
if (!specifier)
|
|
91
|
+
return;
|
|
92
|
+
const { reference, isLocalStyleImport } = this.resolveStyleImportReference(specifier, styleFile);
|
|
93
|
+
if (!isLocalStyleImport) {
|
|
82
94
|
return;
|
|
83
95
|
}
|
|
84
|
-
imports.push(
|
|
85
|
-
importer: path.resolve(styleFile),
|
|
86
|
-
imported: path.resolve(path.dirname(styleFile), specifier),
|
|
87
|
-
});
|
|
96
|
+
imports.push(reference);
|
|
88
97
|
});
|
|
89
98
|
}
|
|
90
99
|
return imports;
|
|
@@ -102,17 +111,117 @@ export class StyleProcessor {
|
|
|
102
111
|
}
|
|
103
112
|
return specifiers;
|
|
104
113
|
}
|
|
114
|
+
assertNoLocalStyleImportCycles(styleFiles) {
|
|
115
|
+
const visited = new Set();
|
|
116
|
+
const visiting = new Set();
|
|
117
|
+
const stack = [];
|
|
118
|
+
const visit = (styleFile) => {
|
|
119
|
+
const normalizedPath = path.resolve(styleFile);
|
|
120
|
+
if (visited.has(normalizedPath))
|
|
121
|
+
return;
|
|
122
|
+
if (visiting.has(normalizedPath)) {
|
|
123
|
+
const cycleStart = stack.indexOf(normalizedPath);
|
|
124
|
+
const cycle = cycleStart >= 0
|
|
125
|
+
? [...stack.slice(cycleStart), normalizedPath]
|
|
126
|
+
: [normalizedPath, normalizedPath];
|
|
127
|
+
throw new Error(`[css] circular CSS import detected: ${cycle.join(' -> ')}`);
|
|
128
|
+
}
|
|
129
|
+
if (!fs.existsSync(normalizedPath))
|
|
130
|
+
return;
|
|
131
|
+
visiting.add(normalizedPath);
|
|
132
|
+
stack.push(normalizedPath);
|
|
133
|
+
for (const reference of this.collectLocalStyleImportReferences(normalizedPath)) {
|
|
134
|
+
visit(reference.imported);
|
|
135
|
+
}
|
|
136
|
+
stack.pop();
|
|
137
|
+
visiting.delete(normalizedPath);
|
|
138
|
+
visited.add(normalizedPath);
|
|
139
|
+
};
|
|
140
|
+
for (const styleFile of styleFiles) {
|
|
141
|
+
visit(styleFile);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
assertNoSourceRootEscapingLocalStyleImports(styleFiles) {
|
|
145
|
+
for (const styleFile of styleFiles) {
|
|
146
|
+
if (!fs.existsSync(styleFile))
|
|
147
|
+
continue;
|
|
148
|
+
for (const reference of this.collectLocalStyleImportReferences(path.resolve(styleFile))) {
|
|
149
|
+
this.assertSourceRootLocalStyleImport(reference, true);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
105
153
|
parse(code, from) {
|
|
106
154
|
// Keep parsing behind one method so future style languages can transform
|
|
107
155
|
// to CSS before PostCSS reads the final stylesheet.
|
|
108
156
|
return postcss.parse(code, { from });
|
|
109
157
|
}
|
|
110
158
|
parseImportSpecifier(params) {
|
|
159
|
+
return this.parseImportSpecifierReference(params)?.specifier ?? null;
|
|
160
|
+
}
|
|
161
|
+
resolveStyleImportReference(specifier, stylePath) {
|
|
162
|
+
const fromDir = path.dirname(stylePath);
|
|
163
|
+
const sourceStylePath = this.resolver.resolveSourceStyleDependency(specifier, fromDir);
|
|
164
|
+
const isSourceStyleSpecifier = this.isSourceStyleImportSpecifier(specifier, sourceStylePath);
|
|
165
|
+
if (isSourceStyleSpecifier) {
|
|
166
|
+
if (!sourceStylePath || !fs.existsSync(sourceStylePath)) {
|
|
167
|
+
this.throwMissingLocalStyleImport(specifier, stylePath);
|
|
168
|
+
}
|
|
169
|
+
return {
|
|
170
|
+
reference: {
|
|
171
|
+
importer: path.resolve(stylePath),
|
|
172
|
+
imported: path.resolve(sourceStylePath),
|
|
173
|
+
specifier,
|
|
174
|
+
},
|
|
175
|
+
isLocalStyleImport: true,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
const importedPath = this.resolver.resolveStyleDependency(specifier, fromDir);
|
|
179
|
+
return {
|
|
180
|
+
reference: {
|
|
181
|
+
importer: path.resolve(stylePath),
|
|
182
|
+
imported: path.resolve(importedPath),
|
|
183
|
+
specifier,
|
|
184
|
+
},
|
|
185
|
+
isLocalStyleImport: false,
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
assertSourceRootLocalStyleImport(reference, isLocalStyleImport) {
|
|
189
|
+
if (!isLocalStyleImport ||
|
|
190
|
+
!this.resolver.isInsideSourceRoot(reference.importer) ||
|
|
191
|
+
this.resolver.isInsideSourceRoot(reference.imported)) {
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
throw new Error(`[css] local CSS import escapes source root: ${reference.specifier} from ${reference.importer}`);
|
|
195
|
+
}
|
|
196
|
+
collectLocalStyleImportReferences(styleFile) {
|
|
197
|
+
const imports = [];
|
|
198
|
+
const css = fs.readFileSync(styleFile, 'utf8');
|
|
199
|
+
const root = this.parse(css, styleFile);
|
|
200
|
+
root.walkAtRules('import', (rule) => {
|
|
201
|
+
const specifier = this.parseImportSpecifier(rule.params);
|
|
202
|
+
if (!specifier)
|
|
203
|
+
return;
|
|
204
|
+
const { reference, isLocalStyleImport } = this.resolveStyleImportReference(specifier, styleFile);
|
|
205
|
+
if (!isLocalStyleImport) {
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
imports.push(reference);
|
|
209
|
+
});
|
|
210
|
+
return imports;
|
|
211
|
+
}
|
|
212
|
+
parseImportSpecifierReference(params) {
|
|
111
213
|
const value = params.trim();
|
|
214
|
+
const valueOffset = params.indexOf(value);
|
|
112
215
|
const first = value[0];
|
|
113
216
|
if (first === '"' || first === "'") {
|
|
114
217
|
const end = value.indexOf(first, 1);
|
|
115
|
-
return end > 0
|
|
218
|
+
return end > 0
|
|
219
|
+
? {
|
|
220
|
+
specifier: value.slice(1, end),
|
|
221
|
+
specifierStart: valueOffset + 1,
|
|
222
|
+
specifierEnd: valueOffset + end,
|
|
223
|
+
}
|
|
224
|
+
: null;
|
|
116
225
|
}
|
|
117
226
|
if (!value.startsWith('url(')) {
|
|
118
227
|
return null;
|
|
@@ -124,8 +233,24 @@ export class StyleProcessor {
|
|
|
124
233
|
const quote = url[0];
|
|
125
234
|
if (quote === '"' || quote === "'") {
|
|
126
235
|
const quoteEnd = url.indexOf(quote, 1);
|
|
127
|
-
|
|
236
|
+
const quotedValueStart = value.indexOf(quote, 4);
|
|
237
|
+
return quoteEnd > 0
|
|
238
|
+
? {
|
|
239
|
+
specifier: url.slice(1, quoteEnd),
|
|
240
|
+
specifierStart: valueOffset + quotedValueStart + 1,
|
|
241
|
+
specifierEnd: valueOffset + quotedValueStart + quoteEnd,
|
|
242
|
+
}
|
|
243
|
+
: null;
|
|
128
244
|
}
|
|
129
|
-
|
|
245
|
+
if (!url)
|
|
246
|
+
return null;
|
|
247
|
+
const rawUrl = value.slice(4, end);
|
|
248
|
+
const leadingSpaces = rawUrl.length - rawUrl.trimStart().length;
|
|
249
|
+
const trailingSpaces = rawUrl.length - rawUrl.trimEnd().length;
|
|
250
|
+
return {
|
|
251
|
+
specifier: url,
|
|
252
|
+
specifierStart: valueOffset + 4 + leadingSpaces,
|
|
253
|
+
specifierEnd: valueOffset + end - trailingSpaces,
|
|
254
|
+
};
|
|
130
255
|
}
|
|
131
256
|
}
|
|
@@ -3,8 +3,13 @@ export declare class WorkspaceStyleResolver {
|
|
|
3
3
|
private readonly config;
|
|
4
4
|
private readonly context;
|
|
5
5
|
private readonly require;
|
|
6
|
+
readonly sourceRoot: string;
|
|
6
7
|
constructor(config: ModuleStyleBuildConfig, context: ResolvedModuleStyleBuildContext);
|
|
7
8
|
resolveStyleDependency(specifier: string, fromDir?: string): string;
|
|
9
|
+
resolveSourceStyleDependency(specifier: string, fromDir?: string): string | null;
|
|
10
|
+
isInsideSourceRoot(file: string): boolean;
|
|
11
|
+
isStyleFile(file: string): boolean;
|
|
8
12
|
toOutputStyleSpecifier(specifier: string, outRoot: string): string;
|
|
9
13
|
toExternalStyleSpecifier(specifier: string, outRoot: string): string;
|
|
14
|
+
private resolveSourceImportPaths;
|
|
10
15
|
}
|
|
@@ -1,20 +1,26 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
3
|
import { NODE_MODULES_DIR } from '#auklet/css/constants';
|
|
4
|
+
import { resolvePackageImportsSourceImport } from '#auklet/css/core/resolvers/packageImports';
|
|
5
|
+
import { resolveTsconfigPathsSourceImport } from '#auklet/css/core/resolvers/tsconfigPaths';
|
|
4
6
|
import { createExternalStyleSpecifier, createOutputStyleSpecifier, } from '#auklet/css/core/style/specifier';
|
|
5
7
|
export class WorkspaceStyleResolver {
|
|
6
8
|
config;
|
|
7
9
|
context;
|
|
8
10
|
require;
|
|
11
|
+
sourceRoot;
|
|
9
12
|
constructor(config, context) {
|
|
10
13
|
this.config = config;
|
|
11
14
|
this.context = context;
|
|
15
|
+
this.sourceRoot = path.isAbsolute(context.sourceDir)
|
|
16
|
+
? context.sourceDir
|
|
17
|
+
: path.join(context.packageRoot, context.sourceDir);
|
|
12
18
|
this.require = createRequire(path.join(this.context.packageRoot, 'package.json'));
|
|
13
19
|
}
|
|
14
20
|
resolveStyleDependency(specifier, fromDir = this.context.packageRoot) {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
21
|
+
const sourceStyleDependency = this.resolveSourceStyleDependency(specifier, fromDir);
|
|
22
|
+
if (sourceStyleDependency)
|
|
23
|
+
return sourceStyleDependency;
|
|
18
24
|
try {
|
|
19
25
|
return this.require.resolve(specifier, {
|
|
20
26
|
paths: [this.context.packageRoot],
|
|
@@ -24,6 +30,26 @@ export class WorkspaceStyleResolver {
|
|
|
24
30
|
return path.resolve(this.context.packageRoot, NODE_MODULES_DIR, specifier);
|
|
25
31
|
}
|
|
26
32
|
}
|
|
33
|
+
resolveSourceStyleDependency(specifier, fromDir = this.context.packageRoot) {
|
|
34
|
+
if (specifier.startsWith('.')) {
|
|
35
|
+
return path.resolve(fromDir, specifier);
|
|
36
|
+
}
|
|
37
|
+
for (const sourceRelativePath of this.resolveSourceImportPaths(specifier)) {
|
|
38
|
+
const file = path.join(this.sourceRoot, sourceRelativePath);
|
|
39
|
+
if (this.isStyleFile(file))
|
|
40
|
+
return file;
|
|
41
|
+
}
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
isInsideSourceRoot(file) {
|
|
45
|
+
const relative = path.relative(this.sourceRoot, file);
|
|
46
|
+
return (Boolean(relative) &&
|
|
47
|
+
!relative.startsWith('..') &&
|
|
48
|
+
!path.isAbsolute(relative));
|
|
49
|
+
}
|
|
50
|
+
isStyleFile(file) {
|
|
51
|
+
return this.config.styleExtensions.includes(path.extname(file));
|
|
52
|
+
}
|
|
27
53
|
toOutputStyleSpecifier(specifier, outRoot) {
|
|
28
54
|
return createOutputStyleSpecifier(specifier, {
|
|
29
55
|
currentOutputFormat: path.basename(outRoot),
|
|
@@ -39,4 +65,10 @@ export class WorkspaceStyleResolver {
|
|
|
39
65
|
externalStyleFile: this.config.output.externalStyleFile,
|
|
40
66
|
});
|
|
41
67
|
}
|
|
68
|
+
resolveSourceImportPaths(specifier) {
|
|
69
|
+
return [
|
|
70
|
+
...resolvePackageImportsSourceImport(this.context.packageRoot, this.sourceRoot, specifier),
|
|
71
|
+
...resolveTsconfigPathsSourceImport(this.context.packageRoot, this.sourceRoot, specifier),
|
|
72
|
+
];
|
|
73
|
+
}
|
|
42
74
|
}
|
|
@@ -21,6 +21,7 @@ export class ModuleStyleBuilder {
|
|
|
21
21
|
const normalizedConfig = normalizeAukletConfig(rawConfig);
|
|
22
22
|
const context = this.createBuildContext(normalizedConfig);
|
|
23
23
|
const packageContext = this.createPackageContext(context, normalizedConfig);
|
|
24
|
+
packageContext.assertNoSourceRootEscapingLocalStyleImports();
|
|
24
25
|
const writerOptions = {
|
|
25
26
|
config: this.config,
|
|
26
27
|
context,
|
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
|
-
import { writeStyleFile, } from '#auklet/css/production/format/shared';
|
|
2
|
+
import { toRelativeImportSpecifier, writeStyleFile, } from '#auklet/css/production/format/shared';
|
|
3
3
|
export class ModuleStyleWriter {
|
|
4
|
+
sourceRoot;
|
|
4
5
|
config;
|
|
5
6
|
packageContext;
|
|
6
7
|
styleProcessor;
|
|
7
8
|
constructor(options) {
|
|
8
9
|
this.config = options.config;
|
|
9
10
|
this.packageContext = options.packageContext;
|
|
11
|
+
this.sourceRoot = options.packageContext.sourceRoot;
|
|
10
12
|
this.styleProcessor = options.packageContext.styleProcessor;
|
|
11
13
|
}
|
|
12
14
|
write(outRoot) {
|
|
13
15
|
const target = path.join(outRoot, this.config.output.styleDir, this.config.output.moduleStyleFile);
|
|
14
|
-
const
|
|
16
|
+
const targetDir = path.dirname(target);
|
|
15
17
|
const root = this.styleProcessor.createRoot();
|
|
16
|
-
for (const styleFile of this.packageContext.
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
this.styleProcessor.appendStyleContent(root, content, styleFile);
|
|
20
|
-
}
|
|
18
|
+
for (const styleFile of this.packageContext.getStyleEntryFiles()) {
|
|
19
|
+
const outputStyleFile = path.join(outRoot, path.relative(this.sourceRoot, styleFile));
|
|
20
|
+
this.styleProcessor.appendImportRule(root, toRelativeImportSpecifier(targetDir, outputStyleFile));
|
|
21
21
|
}
|
|
22
22
|
if (!root.nodes?.length)
|
|
23
23
|
return null;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type FormatWriterOptions } from '#auklet/css/production/format/shared';
|
|
2
2
|
export declare class SourceStyleFileWriter {
|
|
3
3
|
private readonly sourceRoot;
|
|
4
|
-
private readonly
|
|
4
|
+
private readonly resolver;
|
|
5
5
|
private readonly styleProcessor;
|
|
6
6
|
constructor(options: FormatWriterOptions);
|
|
7
7
|
copy(files: Array<string>, outRoot: string): void;
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
|
-
import { writeStyleFile, } from '#auklet/css/production/format/shared';
|
|
2
|
+
import { toRelativeImportSpecifier, writeStyleFile, } from '#auklet/css/production/format/shared';
|
|
3
3
|
export class SourceStyleFileWriter {
|
|
4
4
|
sourceRoot;
|
|
5
|
-
|
|
5
|
+
resolver;
|
|
6
6
|
styleProcessor;
|
|
7
7
|
constructor(options) {
|
|
8
8
|
this.sourceRoot = options.packageContext.sourceRoot;
|
|
9
|
-
this.
|
|
9
|
+
this.resolver = options.packageContext.resolver;
|
|
10
10
|
this.styleProcessor = options.packageContext.styleProcessor;
|
|
11
11
|
}
|
|
12
12
|
copy(files, outRoot) {
|
|
@@ -14,7 +14,13 @@ export class SourceStyleFileWriter {
|
|
|
14
14
|
const relative = path.relative(this.sourceRoot, sourceFile);
|
|
15
15
|
const target = path.join(outRoot, relative);
|
|
16
16
|
const content = this.styleProcessor.readStyleFile(sourceFile, undefined, {
|
|
17
|
-
|
|
17
|
+
mapImportSpecifier: (reference) => {
|
|
18
|
+
if (!this.resolver.isInsideSourceRoot(reference.imported)) {
|
|
19
|
+
return reference.specifier;
|
|
20
|
+
}
|
|
21
|
+
return toRelativeImportSpecifier(path.dirname(target), path.join(outRoot, path.relative(this.sourceRoot, reference.imported)));
|
|
22
|
+
},
|
|
23
|
+
shouldExpandImport: () => false,
|
|
18
24
|
});
|
|
19
25
|
writeStyleFile(target, content);
|
|
20
26
|
}
|
|
@@ -29,6 +29,7 @@ export class ModuleStyleOutputWriter {
|
|
|
29
29
|
this.moduleEntryWriter = new ModuleStyleEntryWriter(options);
|
|
30
30
|
}
|
|
31
31
|
write() {
|
|
32
|
+
this.packageContext.assertPreservedLocalStyleImports();
|
|
32
33
|
const moduleEntries = this.createModuleEntries();
|
|
33
34
|
const outputs = [];
|
|
34
35
|
for (const format of this.config.output.outputFormats) {
|
|
@@ -10,10 +10,15 @@ export declare class StyleCodeFactory {
|
|
|
10
10
|
private createDependencyStyleCode;
|
|
11
11
|
private createExternalStyleCode;
|
|
12
12
|
private createThemeStyleCode;
|
|
13
|
+
private createFullModuleStyleCode;
|
|
13
14
|
private createModuleStyleCode;
|
|
14
15
|
private createSourceModuleStyleCode;
|
|
16
|
+
private createOwnSourceStyleCode;
|
|
15
17
|
private toDevModuleImportSpecifier;
|
|
16
18
|
private toDevExternalStyleSpecifier;
|
|
17
19
|
private parsePackageStyleIdInRequest;
|
|
18
20
|
private withDependencyPackage;
|
|
21
|
+
private normalizeTopLevelImports;
|
|
22
|
+
private isTopLevelImportRule;
|
|
23
|
+
private isImportPreludeNode;
|
|
19
24
|
}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
|
+
import postcss from 'postcss';
|
|
2
3
|
import { mergeLoadResults } from '#auklet/css/vite/moduleGraph/loadResult';
|
|
3
4
|
import { parsePackageStyleId } from '#auklet/css/vite/moduleGraph/styleId';
|
|
4
5
|
import { toDevDependencyImportSpecifier } from '#auklet/css/vite/moduleGraph/devDependency';
|
|
5
6
|
import { EXTERNAL_ENTRY, MODULE_ENTRY, STYLE_ENTRY, THEMES_ENTRY_PREFIX, } from '#auklet/css/constants';
|
|
6
7
|
import { createModuleStyleEntryPlan, createExternalEntryParts, createStyleEntryParts, createThemeEntryParts, } from '#auklet/css/core/style/entries';
|
|
7
8
|
import { createDevExternalStyleSpecifier, createDevModuleStyleSpecifier, createImportCode, removeStyleExtension, } from '#auklet/css/core/style/specifier';
|
|
9
|
+
import { toFsSpecifier } from '#auklet/utils';
|
|
8
10
|
// 生成 Vite/dev 虚拟 CSS;production writer 共享入口语义,但写入真实文件。
|
|
9
11
|
export class StyleCodeFactory {
|
|
10
12
|
config;
|
|
@@ -12,7 +14,7 @@ export class StyleCodeFactory {
|
|
|
12
14
|
this.config = config;
|
|
13
15
|
}
|
|
14
16
|
async createPackageStyleCode(parsed, cache) {
|
|
15
|
-
return cache.getLoadResult(parsed, () => this.createUncachedPackageStyleCode(parsed, cache));
|
|
17
|
+
return cache.getLoadResult(parsed, async () => this.normalizeTopLevelImports(await this.createUncachedPackageStyleCode(parsed, cache)));
|
|
16
18
|
}
|
|
17
19
|
async createUncachedPackageStyleCode(parsed, cache) {
|
|
18
20
|
const context = await cache.getContext(parsed);
|
|
@@ -43,7 +45,7 @@ export class StyleCodeFactory {
|
|
|
43
45
|
else {
|
|
44
46
|
result = await this.createSourceModuleStyleCode(context, cache, parsed.stylePath);
|
|
45
47
|
}
|
|
46
|
-
return cache.writePersistentLoadResult(parsed, context, result);
|
|
48
|
+
return cache.writePersistentLoadResult(parsed, context, this.normalizeTopLevelImports(result));
|
|
47
49
|
}
|
|
48
50
|
async createStyleCode(context, cache) {
|
|
49
51
|
const results = [];
|
|
@@ -56,7 +58,7 @@ export class StyleCodeFactory {
|
|
|
56
58
|
results.push(await this.createThemeStyleCode(context, cache, part.themeNames, false));
|
|
57
59
|
continue;
|
|
58
60
|
}
|
|
59
|
-
results.push(this.
|
|
61
|
+
results.push(this.createFullModuleStyleCode(context));
|
|
60
62
|
}
|
|
61
63
|
return mergeLoadResults(...results);
|
|
62
64
|
}
|
|
@@ -124,7 +126,7 @@ export class StyleCodeFactory {
|
|
|
124
126
|
watchFiles: [],
|
|
125
127
|
});
|
|
126
128
|
}
|
|
127
|
-
|
|
129
|
+
createFullModuleStyleCode(context) {
|
|
128
130
|
const { styleFiles } = context.packageContext;
|
|
129
131
|
const root = context.styleProcessor.createRoot();
|
|
130
132
|
const seen = new Set();
|
|
@@ -139,7 +141,19 @@ export class StyleCodeFactory {
|
|
|
139
141
|
watchFiles: [...context.configPaths, ...styleFiles],
|
|
140
142
|
};
|
|
141
143
|
}
|
|
144
|
+
createModuleStyleCode(context) {
|
|
145
|
+
context.packageContext.assertPreservedLocalStyleImports();
|
|
146
|
+
const { styleFiles } = context.packageContext;
|
|
147
|
+
const imports = context.packageContext
|
|
148
|
+
.getStyleEntryFiles()
|
|
149
|
+
.map((styleFile) => toFsSpecifier(styleFile));
|
|
150
|
+
return {
|
|
151
|
+
code: createImportCode(imports),
|
|
152
|
+
watchFiles: [...context.configPaths, ...styleFiles],
|
|
153
|
+
};
|
|
154
|
+
}
|
|
142
155
|
async createSourceModuleStyleCode(context, cache, stylePath) {
|
|
156
|
+
context.packageContext.assertPreservedLocalStyleImports();
|
|
143
157
|
const sourceModuleDir = removeStyleExtension(stylePath);
|
|
144
158
|
const { styleFiles, sourceFiles } = context.packageContext;
|
|
145
159
|
const entry = createModuleStyleEntryPlan(context.packageContext, sourceModuleDir);
|
|
@@ -152,8 +166,12 @@ export class StyleCodeFactory {
|
|
|
152
166
|
const result = this.toDevModuleImportSpecifier(specifier, context, cache, sourceStyleDir);
|
|
153
167
|
const parsed = this.parsePackageStyleIdInRequest(result, cache);
|
|
154
168
|
if (parsed) {
|
|
155
|
-
const
|
|
156
|
-
moduleStyleResults.push(this.withDependencyPackage(
|
|
169
|
+
const dependencyResult = await this.createPackageStyleCode(parsed, cache);
|
|
170
|
+
moduleStyleResults.push(this.withDependencyPackage({
|
|
171
|
+
...dependencyResult,
|
|
172
|
+
code: '',
|
|
173
|
+
}, parsed.packageName));
|
|
174
|
+
moduleStyleSpecifiers.push(result);
|
|
157
175
|
continue;
|
|
158
176
|
}
|
|
159
177
|
const resolvedSpecifier = toDevDependencyImportSpecifier(context, result);
|
|
@@ -163,17 +181,7 @@ export class StyleCodeFactory {
|
|
|
163
181
|
moduleStyleWatchFiles.push(resolvedSpecifier.watchFile);
|
|
164
182
|
}
|
|
165
183
|
}
|
|
166
|
-
const
|
|
167
|
-
const seen = new Set();
|
|
168
|
-
for (const ownStyleFile of entry.ownStyleFiles) {
|
|
169
|
-
const content = context.styleProcessor.readStyleFile(ownStyleFile, seen);
|
|
170
|
-
if (content.trim()) {
|
|
171
|
-
context.styleProcessor.appendStyleContent(root, content, ownStyleFile);
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
const ownStyleCode = root.nodes?.length
|
|
175
|
-
? context.styleProcessor.stringify(root)
|
|
176
|
-
: '';
|
|
184
|
+
const ownStyleCode = this.createOwnSourceStyleCode(context, entry.ownStyleFiles);
|
|
177
185
|
return mergeLoadResults(...moduleStyleResults, {
|
|
178
186
|
code: [createImportCode(moduleStyleSpecifiers), ownStyleCode]
|
|
179
187
|
.filter((code) => code.trim())
|
|
@@ -187,6 +195,24 @@ export class StyleCodeFactory {
|
|
|
187
195
|
cacheInputFiles: moduleStyleCacheInputFiles,
|
|
188
196
|
});
|
|
189
197
|
}
|
|
198
|
+
createOwnSourceStyleCode(context, styleFiles) {
|
|
199
|
+
const root = context.styleProcessor.createRoot();
|
|
200
|
+
for (const styleFile of styleFiles) {
|
|
201
|
+
const content = context.styleProcessor.readStyleFile(styleFile, undefined, {
|
|
202
|
+
mapImportSpecifier: (reference) => {
|
|
203
|
+
if (!context.resolver.isInsideSourceRoot(reference.imported)) {
|
|
204
|
+
return reference.specifier;
|
|
205
|
+
}
|
|
206
|
+
return toFsSpecifier(reference.imported);
|
|
207
|
+
},
|
|
208
|
+
shouldExpandImport: () => false,
|
|
209
|
+
});
|
|
210
|
+
if (content.trim()) {
|
|
211
|
+
context.styleProcessor.appendStyleContent(root, content, styleFile);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
return root.nodes?.length ? context.styleProcessor.stringify(root) : '';
|
|
215
|
+
}
|
|
190
216
|
toDevModuleImportSpecifier(specifier, context, cache, sourceStyleDir) {
|
|
191
217
|
return createDevModuleStyleSpecifier(specifier, {
|
|
192
218
|
sourceStyleDir,
|
|
@@ -214,4 +240,65 @@ export class StyleCodeFactory {
|
|
|
214
240
|
dependencyPackages: Array.from(new Set([packageName, ...(result.dependencyPackages ?? [])])),
|
|
215
241
|
};
|
|
216
242
|
}
|
|
243
|
+
normalizeTopLevelImports(result) {
|
|
244
|
+
if (!result.code.includes('@import'))
|
|
245
|
+
return result;
|
|
246
|
+
const lateImports = [];
|
|
247
|
+
const removedImports = [];
|
|
248
|
+
const seenImportParams = new Set();
|
|
249
|
+
const root = postcss.parse(result.code);
|
|
250
|
+
let hasChanges = false;
|
|
251
|
+
let hasSeenStyleNode = false;
|
|
252
|
+
for (const node of root.nodes ?? []) {
|
|
253
|
+
if (this.isImportPreludeNode(node))
|
|
254
|
+
continue;
|
|
255
|
+
if (this.isTopLevelImportRule(node)) {
|
|
256
|
+
if (seenImportParams.has(node.params)) {
|
|
257
|
+
hasChanges = true;
|
|
258
|
+
removedImports.push(node);
|
|
259
|
+
continue;
|
|
260
|
+
}
|
|
261
|
+
seenImportParams.add(node.params);
|
|
262
|
+
if (hasSeenStyleNode) {
|
|
263
|
+
hasChanges = true;
|
|
264
|
+
lateImports.push(node.clone());
|
|
265
|
+
removedImports.push(node);
|
|
266
|
+
}
|
|
267
|
+
continue;
|
|
268
|
+
}
|
|
269
|
+
hasSeenStyleNode = true;
|
|
270
|
+
}
|
|
271
|
+
if (!hasChanges)
|
|
272
|
+
return result;
|
|
273
|
+
for (const node of removedImports) {
|
|
274
|
+
node.remove();
|
|
275
|
+
}
|
|
276
|
+
const firstStyleNode = root.nodes?.find((node) => !this.isImportPreludeNode(node) && !this.isTopLevelImportRule(node));
|
|
277
|
+
if (firstStyleNode) {
|
|
278
|
+
for (const rule of lateImports) {
|
|
279
|
+
firstStyleNode.before(rule);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
else {
|
|
283
|
+
for (const rule of lateImports) {
|
|
284
|
+
root.append(rule);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
return {
|
|
288
|
+
...result,
|
|
289
|
+
code: root.toString(),
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
isTopLevelImportRule(node) {
|
|
293
|
+
return node.type === 'atrule' && node.name === 'import';
|
|
294
|
+
}
|
|
295
|
+
isImportPreludeNode(node) {
|
|
296
|
+
if (node.type === 'comment')
|
|
297
|
+
return true;
|
|
298
|
+
if (node.type !== 'atrule')
|
|
299
|
+
return false;
|
|
300
|
+
if (node.name === 'charset')
|
|
301
|
+
return true;
|
|
302
|
+
return node.name === 'layer' && !node.nodes?.length;
|
|
303
|
+
}
|
|
217
304
|
}
|