@knighted/css 1.0.0-rc.8 → 1.0.0-rc.9
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/dist/cjs/css.cjs +19 -15
- package/dist/cjs/css.d.cts +5 -4
- package/dist/cjs/moduleGraph.cjs +431 -0
- package/dist/cjs/moduleGraph.d.cts +15 -0
- package/dist/cjs/sassInternals.cjs +6 -3
- package/dist/cjs/sassInternals.d.cts +3 -4
- package/dist/cjs/types.cjs +2 -0
- package/dist/cjs/types.d.cts +4 -0
- package/dist/css.d.ts +5 -4
- package/dist/css.js +19 -15
- package/dist/moduleGraph.d.ts +15 -0
- package/dist/moduleGraph.js +425 -0
- package/dist/sassInternals.d.ts +3 -4
- package/dist/sassInternals.js +6 -3
- package/dist/types.d.ts +4 -0
- package/dist/types.js +1 -0
- package/package.json +6 -8
package/dist/cjs/css.cjs
CHANGED
|
@@ -9,9 +9,9 @@ exports.cssWithMeta = cssWithMeta;
|
|
|
9
9
|
exports.compileVanillaModule = compileVanillaModule;
|
|
10
10
|
const node_path_1 = __importDefault(require("node:path"));
|
|
11
11
|
const node_fs_1 = require("node:fs");
|
|
12
|
-
const dependency_tree_1 = __importDefault(require("dependency-tree"));
|
|
13
12
|
const lightningcss_1 = require("lightningcss");
|
|
14
13
|
const helpers_js_1 = require("./helpers.cjs");
|
|
14
|
+
const moduleGraph_js_1 = require("./moduleGraph.cjs");
|
|
15
15
|
const sassInternals_js_1 = require("./sassInternals.cjs");
|
|
16
16
|
exports.DEFAULT_EXTENSIONS = ['.css', '.scss', '.sass', '.less', '.css.ts'];
|
|
17
17
|
async function css(entry, options = {}) {
|
|
@@ -22,11 +22,12 @@ async function cssWithMeta(entry, options = {}) {
|
|
|
22
22
|
const cwd = options.cwd ? node_path_1.default.resolve(options.cwd) : process.cwd();
|
|
23
23
|
const entryPath = await resolveEntry(entry, cwd, options.resolver);
|
|
24
24
|
const extensions = (options.extensions ?? exports.DEFAULT_EXTENSIONS).map(ext => ext.toLowerCase());
|
|
25
|
-
const files = collectStyleDependencies(entryPath, {
|
|
25
|
+
const files = await collectStyleDependencies(entryPath, {
|
|
26
26
|
cwd,
|
|
27
27
|
extensions,
|
|
28
28
|
filter: options.filter,
|
|
29
|
-
|
|
29
|
+
graphOptions: options.moduleGraph,
|
|
30
|
+
resolver: options.resolver,
|
|
30
31
|
});
|
|
31
32
|
if (files.length === 0) {
|
|
32
33
|
return { css: '', files: [] };
|
|
@@ -76,30 +77,33 @@ async function resolveEntry(entry, cwd, resolver) {
|
|
|
76
77
|
}
|
|
77
78
|
return node_path_1.default.resolve(cwd, entry);
|
|
78
79
|
}
|
|
79
|
-
function collectStyleDependencies(entryPath, { cwd, extensions, filter,
|
|
80
|
+
async function collectStyleDependencies(entryPath, { cwd, extensions, filter, graphOptions, resolver, }) {
|
|
80
81
|
const seen = new Set();
|
|
81
82
|
const order = [];
|
|
82
83
|
const shouldInclude = typeof filter === 'function'
|
|
83
84
|
? filter
|
|
84
85
|
: (filePath) => !filePath.includes('node_modules');
|
|
85
86
|
const entryIsStyle = Boolean(matchExtension(entryPath, extensions));
|
|
86
|
-
let
|
|
87
|
+
let discoveredStyles = [];
|
|
87
88
|
if (!entryIsStyle) {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
directory: cwd,
|
|
89
|
+
discoveredStyles = await (0, moduleGraph_js_1.collectStyleImports)(entryPath, {
|
|
90
|
+
cwd,
|
|
91
|
+
styleExtensions: extensions,
|
|
92
92
|
filter: shouldInclude,
|
|
93
|
-
|
|
94
|
-
|
|
93
|
+
resolver,
|
|
94
|
+
graphOptions,
|
|
95
|
+
});
|
|
95
96
|
}
|
|
96
|
-
const candidates = entryIsStyle ? [entryPath] : [entryPath, ...
|
|
97
|
+
const candidates = entryIsStyle ? [entryPath] : [entryPath, ...discoveredStyles];
|
|
97
98
|
for (const candidate of candidates) {
|
|
98
99
|
const match = matchExtension(candidate, extensions);
|
|
99
|
-
if (!match
|
|
100
|
+
if (!match)
|
|
101
|
+
continue;
|
|
102
|
+
const resolvedCandidate = node_path_1.default.resolve(candidate);
|
|
103
|
+
if (seen.has(resolvedCandidate))
|
|
100
104
|
continue;
|
|
101
|
-
seen.add(
|
|
102
|
-
order.push({ path:
|
|
105
|
+
seen.add(resolvedCandidate);
|
|
106
|
+
order.push({ path: resolvedCandidate, ext: match });
|
|
103
107
|
}
|
|
104
108
|
return order;
|
|
105
109
|
}
|
package/dist/cjs/css.d.cts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import type { Options as DependencyTreeOpts } from 'dependency-tree';
|
|
2
1
|
import { type TransformOptions as LightningTransformOptions } from 'lightningcss';
|
|
3
2
|
import { type SpecificitySelector, type SpecificityStrategy } from './helpers.cjs';
|
|
4
|
-
import type {
|
|
5
|
-
|
|
3
|
+
import type { ModuleGraphOptions } from './moduleGraph.cjs';
|
|
4
|
+
import type { CssResolver } from './types.cjs';
|
|
5
|
+
export type { CssResolver } from './types.cjs';
|
|
6
|
+
export type { ModuleGraphOptions } from './moduleGraph.cjs';
|
|
6
7
|
export declare const DEFAULT_EXTENSIONS: string[];
|
|
7
8
|
type LightningCssConfig = boolean | Partial<Omit<LightningTransformOptions<never>, 'code'>>;
|
|
8
9
|
type PeerLoader = (name: string) => Promise<unknown>;
|
|
@@ -16,7 +17,7 @@ export interface CssOptions {
|
|
|
16
17
|
strategy?: SpecificityStrategy;
|
|
17
18
|
match?: SpecificitySelector[];
|
|
18
19
|
};
|
|
19
|
-
|
|
20
|
+
moduleGraph?: ModuleGraphOptions;
|
|
20
21
|
resolver?: CssResolver;
|
|
21
22
|
peerResolver?: PeerLoader;
|
|
22
23
|
}
|
|
@@ -0,0 +1,431 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.collectStyleImports = collectStyleImports;
|
|
7
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
8
|
+
const node_module_1 = require("node:module");
|
|
9
|
+
const node_fs_1 = require("node:fs");
|
|
10
|
+
const node_url_1 = require("node:url");
|
|
11
|
+
const oxc_parser_1 = require("oxc-parser");
|
|
12
|
+
const oxc_resolver_1 = require("oxc-resolver");
|
|
13
|
+
const tsconfig_paths_1 = require("tsconfig-paths");
|
|
14
|
+
const get_tsconfig_1 = require("get-tsconfig");
|
|
15
|
+
const SCRIPT_EXTENSIONS = ['.ts', '.tsx', '.mts', '.cts', '.js', '.jsx', '.mjs', '.cjs'];
|
|
16
|
+
const BUILTIN_SPECIFIERS = new Set([
|
|
17
|
+
...node_module_1.builtinModules,
|
|
18
|
+
...node_module_1.builtinModules.map(mod => `node:${mod}`),
|
|
19
|
+
]);
|
|
20
|
+
const tsconfigResultCache = new Map();
|
|
21
|
+
const tsconfigFsCache = new Map();
|
|
22
|
+
async function collectStyleImports(entryPath, options) {
|
|
23
|
+
const { cwd, styleExtensions, filter, resolver, graphOptions } = options;
|
|
24
|
+
const normalizedStyles = normalizeExtensions(styleExtensions);
|
|
25
|
+
const scriptExtensions = normalizeExtensions([
|
|
26
|
+
...SCRIPT_EXTENSIONS,
|
|
27
|
+
...(graphOptions?.extensions ?? []),
|
|
28
|
+
]);
|
|
29
|
+
const resolutionExtensions = dedupeExtensions([
|
|
30
|
+
...scriptExtensions,
|
|
31
|
+
...normalizedStyles,
|
|
32
|
+
]);
|
|
33
|
+
const tsconfigMatcher = createTsconfigMatcher(graphOptions?.tsConfig, cwd, resolutionExtensions);
|
|
34
|
+
const seenScripts = new Set();
|
|
35
|
+
const seenStyles = new Set();
|
|
36
|
+
const styleOrder = [];
|
|
37
|
+
const resolutionCache = new Map();
|
|
38
|
+
const resolverFactory = createResolverFactory(cwd, resolutionExtensions, scriptExtensions, graphOptions);
|
|
39
|
+
async function walk(filePath) {
|
|
40
|
+
const absolutePath = node_path_1.default.resolve(filePath);
|
|
41
|
+
if (seenScripts.has(absolutePath)) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
seenScripts.add(absolutePath);
|
|
45
|
+
const source = await readSourceFile(absolutePath);
|
|
46
|
+
if (!source) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const specifiers = extractModuleSpecifiers(source, absolutePath);
|
|
50
|
+
for (const specifier of specifiers) {
|
|
51
|
+
if (!specifier || isBuiltinSpecifier(specifier)) {
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
const resolved = await resolveImport(specifier, absolutePath);
|
|
55
|
+
if (!resolved) {
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
const normalized = node_path_1.default.resolve(resolved);
|
|
59
|
+
if (!filter(normalized)) {
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
if (isStyleExtension(normalized, normalizedStyles)) {
|
|
63
|
+
if (!seenStyles.has(normalized)) {
|
|
64
|
+
seenStyles.add(normalized);
|
|
65
|
+
styleOrder.push(normalized);
|
|
66
|
+
}
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
if (isScriptExtension(normalized, scriptExtensions)) {
|
|
70
|
+
await walk(normalized);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
async function resolveImport(specifier, importer) {
|
|
75
|
+
const cacheKey = `${importer}::${specifier}`;
|
|
76
|
+
if (resolutionCache.has(cacheKey)) {
|
|
77
|
+
return resolutionCache.get(cacheKey);
|
|
78
|
+
}
|
|
79
|
+
let resolved;
|
|
80
|
+
if (resolver) {
|
|
81
|
+
resolved = normalizeResolverResult(await resolver(specifier, { cwd, from: importer }), cwd);
|
|
82
|
+
}
|
|
83
|
+
if (!resolved && tsconfigMatcher) {
|
|
84
|
+
resolved = tsconfigMatcher(specifier);
|
|
85
|
+
}
|
|
86
|
+
if (!resolved) {
|
|
87
|
+
resolved = resolveWithFactory(resolverFactory, specifier, importer, resolutionExtensions);
|
|
88
|
+
}
|
|
89
|
+
resolutionCache.set(cacheKey, resolved);
|
|
90
|
+
return resolved;
|
|
91
|
+
}
|
|
92
|
+
await walk(entryPath);
|
|
93
|
+
return styleOrder;
|
|
94
|
+
}
|
|
95
|
+
function normalizeExtensions(extensions) {
|
|
96
|
+
const result = new Set();
|
|
97
|
+
for (const ext of extensions) {
|
|
98
|
+
if (!ext)
|
|
99
|
+
continue;
|
|
100
|
+
const normalized = ext.startsWith('.') ? ext.toLowerCase() : `.${ext.toLowerCase()}`;
|
|
101
|
+
result.add(normalized);
|
|
102
|
+
}
|
|
103
|
+
return Array.from(result);
|
|
104
|
+
}
|
|
105
|
+
function dedupeExtensions(extensions) {
|
|
106
|
+
const result = new Set();
|
|
107
|
+
for (const ext of extensions) {
|
|
108
|
+
result.add(ext);
|
|
109
|
+
}
|
|
110
|
+
return Array.from(result);
|
|
111
|
+
}
|
|
112
|
+
function isBuiltinSpecifier(specifier) {
|
|
113
|
+
if (BUILTIN_SPECIFIERS.has(specifier)) {
|
|
114
|
+
return true;
|
|
115
|
+
}
|
|
116
|
+
if (specifier.startsWith('node:')) {
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
function isStyleExtension(filePath, extensions) {
|
|
122
|
+
const lower = filePath.toLowerCase();
|
|
123
|
+
return extensions.some(ext => lower.endsWith(ext));
|
|
124
|
+
}
|
|
125
|
+
function isScriptExtension(filePath, extensions) {
|
|
126
|
+
const ext = node_path_1.default.extname(filePath).toLowerCase();
|
|
127
|
+
return extensions.includes(ext);
|
|
128
|
+
}
|
|
129
|
+
async function readSourceFile(filePath) {
|
|
130
|
+
try {
|
|
131
|
+
return await node_fs_1.promises.readFile(filePath, 'utf8');
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
return undefined;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
function extractModuleSpecifiers(sourceText, filePath) {
|
|
138
|
+
let program;
|
|
139
|
+
try {
|
|
140
|
+
;
|
|
141
|
+
({ program } = (0, oxc_parser_1.parseSync)(filePath, sourceText, { sourceType: 'unambiguous' }));
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
return [];
|
|
145
|
+
}
|
|
146
|
+
const specifiers = [];
|
|
147
|
+
const addSpecifier = (raw) => {
|
|
148
|
+
if (!raw) {
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
const normalized = normalizeSpecifier(raw);
|
|
152
|
+
if (normalized) {
|
|
153
|
+
specifiers.push(normalized);
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
const visitor = new oxc_parser_1.Visitor({
|
|
157
|
+
ImportDeclaration(node) {
|
|
158
|
+
addSpecifier(node.source?.value);
|
|
159
|
+
},
|
|
160
|
+
ExportNamedDeclaration(node) {
|
|
161
|
+
if (node.source) {
|
|
162
|
+
addSpecifier(node.source.value);
|
|
163
|
+
}
|
|
164
|
+
},
|
|
165
|
+
ExportAllDeclaration(node) {
|
|
166
|
+
addSpecifier(node.source?.value);
|
|
167
|
+
},
|
|
168
|
+
TSImportEqualsDeclaration(node) {
|
|
169
|
+
const specifier = extractImportEqualsSpecifier(node);
|
|
170
|
+
if (specifier) {
|
|
171
|
+
addSpecifier(specifier);
|
|
172
|
+
}
|
|
173
|
+
},
|
|
174
|
+
ImportExpression(node) {
|
|
175
|
+
const specifier = getStringFromExpression(node.source);
|
|
176
|
+
if (specifier) {
|
|
177
|
+
addSpecifier(specifier);
|
|
178
|
+
}
|
|
179
|
+
},
|
|
180
|
+
CallExpression(node) {
|
|
181
|
+
if (!isRequireLikeCallee(node.callee)) {
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
const specifier = getStringFromArgument(node.arguments[0]);
|
|
185
|
+
if (specifier) {
|
|
186
|
+
addSpecifier(specifier);
|
|
187
|
+
}
|
|
188
|
+
},
|
|
189
|
+
});
|
|
190
|
+
visitor.visit(program);
|
|
191
|
+
return specifiers;
|
|
192
|
+
}
|
|
193
|
+
function normalizeSpecifier(raw) {
|
|
194
|
+
if (!raw)
|
|
195
|
+
return '';
|
|
196
|
+
const trimmed = raw.trim();
|
|
197
|
+
if (!trimmed || trimmed.startsWith('\0')) {
|
|
198
|
+
return '';
|
|
199
|
+
}
|
|
200
|
+
const queryIndex = trimmed.search(/[?#]/);
|
|
201
|
+
const withoutQuery = queryIndex === -1 ? trimmed : trimmed.slice(0, queryIndex);
|
|
202
|
+
if (!withoutQuery) {
|
|
203
|
+
return '';
|
|
204
|
+
}
|
|
205
|
+
if (/^[a-z][\w+.-]*:/i.test(withoutQuery) && !withoutQuery.startsWith('file:')) {
|
|
206
|
+
return '';
|
|
207
|
+
}
|
|
208
|
+
return withoutQuery;
|
|
209
|
+
}
|
|
210
|
+
function extractImportEqualsSpecifier(node) {
|
|
211
|
+
if (node.moduleReference.type === 'TSExternalModuleReference') {
|
|
212
|
+
return node.moduleReference.expression.value;
|
|
213
|
+
}
|
|
214
|
+
return undefined;
|
|
215
|
+
}
|
|
216
|
+
function getStringFromArgument(argument) {
|
|
217
|
+
if (!argument || argument.type === 'SpreadElement') {
|
|
218
|
+
return undefined;
|
|
219
|
+
}
|
|
220
|
+
return getStringFromExpression(argument);
|
|
221
|
+
}
|
|
222
|
+
function getStringFromExpression(expression) {
|
|
223
|
+
if (!expression) {
|
|
224
|
+
return undefined;
|
|
225
|
+
}
|
|
226
|
+
if (expression.type === 'Literal') {
|
|
227
|
+
const literalValue = expression.value;
|
|
228
|
+
return typeof literalValue === 'string' ? literalValue : undefined;
|
|
229
|
+
}
|
|
230
|
+
if (expression.type === 'TemplateLiteral' && expression.expressions.length === 0) {
|
|
231
|
+
const [first] = expression.quasis;
|
|
232
|
+
return first?.value.cooked ?? first?.value.raw ?? undefined;
|
|
233
|
+
}
|
|
234
|
+
return undefined;
|
|
235
|
+
}
|
|
236
|
+
function isRequireLikeCallee(expression) {
|
|
237
|
+
const target = unwrapExpression(expression);
|
|
238
|
+
if (target.type === 'Identifier') {
|
|
239
|
+
return target.name === 'require';
|
|
240
|
+
}
|
|
241
|
+
if (target.type === 'MemberExpression') {
|
|
242
|
+
const object = target.object;
|
|
243
|
+
if (object.type === 'Identifier') {
|
|
244
|
+
return object.name === 'require';
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
return false;
|
|
248
|
+
}
|
|
249
|
+
function unwrapExpression(expression) {
|
|
250
|
+
if (expression.type === 'ChainExpression') {
|
|
251
|
+
const inner = expression.expression;
|
|
252
|
+
if (inner.type === 'CallExpression') {
|
|
253
|
+
return unwrapExpression(inner.callee);
|
|
254
|
+
}
|
|
255
|
+
return unwrapExpression(inner);
|
|
256
|
+
}
|
|
257
|
+
if (expression.type === 'TSNonNullExpression') {
|
|
258
|
+
return unwrapExpression(expression.expression);
|
|
259
|
+
}
|
|
260
|
+
return expression;
|
|
261
|
+
}
|
|
262
|
+
function normalizeResolverResult(result, cwd) {
|
|
263
|
+
if (!result) {
|
|
264
|
+
return undefined;
|
|
265
|
+
}
|
|
266
|
+
if (result.startsWith('file://')) {
|
|
267
|
+
try {
|
|
268
|
+
return (0, node_url_1.fileURLToPath)(new URL(result));
|
|
269
|
+
}
|
|
270
|
+
catch {
|
|
271
|
+
return undefined;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
return node_path_1.default.isAbsolute(result) ? result : node_path_1.default.resolve(cwd, result);
|
|
275
|
+
}
|
|
276
|
+
function resolveWithFactory(factory, specifier, importer, extensions) {
|
|
277
|
+
if (specifier.startsWith('file://')) {
|
|
278
|
+
try {
|
|
279
|
+
return findExistingFile((0, node_url_1.fileURLToPath)(new URL(specifier)), extensions);
|
|
280
|
+
}
|
|
281
|
+
catch {
|
|
282
|
+
return undefined;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
if (/^[a-z][\w+.-]*:/i.test(specifier)) {
|
|
286
|
+
return undefined;
|
|
287
|
+
}
|
|
288
|
+
try {
|
|
289
|
+
const result = factory.resolveFileSync(importer, specifier);
|
|
290
|
+
return result?.path;
|
|
291
|
+
}
|
|
292
|
+
catch {
|
|
293
|
+
return undefined;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
function createResolverFactory(cwd, extensions, scriptExtensions, graphOptions) {
|
|
297
|
+
const options = {
|
|
298
|
+
extensions,
|
|
299
|
+
conditionNames: graphOptions?.conditions,
|
|
300
|
+
};
|
|
301
|
+
const extensionAlias = buildExtensionAlias(scriptExtensions);
|
|
302
|
+
if (extensionAlias) {
|
|
303
|
+
options.extensionAlias = extensionAlias;
|
|
304
|
+
}
|
|
305
|
+
const tsconfigOption = resolveResolverTsconfig(graphOptions?.tsConfig, cwd);
|
|
306
|
+
if (tsconfigOption) {
|
|
307
|
+
options.tsconfig = tsconfigOption;
|
|
308
|
+
}
|
|
309
|
+
return new oxc_resolver_1.ResolverFactory(options);
|
|
310
|
+
}
|
|
311
|
+
function buildExtensionAlias(scriptExtensions) {
|
|
312
|
+
const alias = {};
|
|
313
|
+
const jsTargets = dedupeExtensions(scriptExtensions.filter(ext => ['.js', '.ts', '.tsx', '.mjs', '.cjs', '.mts', '.cts'].includes(ext)));
|
|
314
|
+
if (jsTargets.length > 0) {
|
|
315
|
+
for (const key of ['.js', '.mjs', '.cjs']) {
|
|
316
|
+
alias[key] = jsTargets;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
const jsxTargets = dedupeExtensions(scriptExtensions.filter(ext => ext === '.jsx' || ext === '.tsx'));
|
|
320
|
+
if (jsxTargets.length > 0) {
|
|
321
|
+
alias['.jsx'] = jsxTargets;
|
|
322
|
+
}
|
|
323
|
+
return Object.keys(alias).length > 0 ? alias : undefined;
|
|
324
|
+
}
|
|
325
|
+
function resolveResolverTsconfig(input, cwd) {
|
|
326
|
+
if (!input || typeof input !== 'string') {
|
|
327
|
+
return undefined;
|
|
328
|
+
}
|
|
329
|
+
const resolved = resolveTsconfigPath(input, cwd);
|
|
330
|
+
if (!resolved) {
|
|
331
|
+
return undefined;
|
|
332
|
+
}
|
|
333
|
+
return { configFile: resolved };
|
|
334
|
+
}
|
|
335
|
+
function createTsconfigMatcher(input, cwd, extensions) {
|
|
336
|
+
const config = loadTsconfigPaths(input, cwd);
|
|
337
|
+
if (!config) {
|
|
338
|
+
return undefined;
|
|
339
|
+
}
|
|
340
|
+
const matchPath = (0, tsconfig_paths_1.createMatchPath)(config.absoluteBaseUrl, config.paths);
|
|
341
|
+
return (specifier) => {
|
|
342
|
+
const matched = matchPath(specifier, undefined, undefined, extensions);
|
|
343
|
+
if (!matched) {
|
|
344
|
+
return undefined;
|
|
345
|
+
}
|
|
346
|
+
return findExistingFile(matched, extensions) ?? matched;
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
function loadTsconfigPaths(input, cwd) {
|
|
350
|
+
if (!input) {
|
|
351
|
+
return undefined;
|
|
352
|
+
}
|
|
353
|
+
if (typeof input === 'string') {
|
|
354
|
+
const target = node_path_1.default.isAbsolute(input) ? input : node_path_1.default.resolve(cwd, input);
|
|
355
|
+
const cached = tsconfigResultCache.get(target);
|
|
356
|
+
if (cached !== undefined) {
|
|
357
|
+
return cached ?? undefined;
|
|
358
|
+
}
|
|
359
|
+
const result = (0, get_tsconfig_1.getTsconfig)(target, undefined, tsconfigFsCache);
|
|
360
|
+
if (!result) {
|
|
361
|
+
tsconfigResultCache.set(target, null);
|
|
362
|
+
return undefined;
|
|
363
|
+
}
|
|
364
|
+
const normalized = normalizeTsconfigCompilerOptions(result.config.compilerOptions, node_path_1.default.dirname(result.path));
|
|
365
|
+
tsconfigResultCache.set(target, normalized ?? null);
|
|
366
|
+
return normalized;
|
|
367
|
+
}
|
|
368
|
+
const compilerOptions = input.compilerOptions;
|
|
369
|
+
return normalizeTsconfigCompilerOptions(compilerOptions, cwd);
|
|
370
|
+
}
|
|
371
|
+
function normalizeTsconfigCompilerOptions(compilerOptions, configDir) {
|
|
372
|
+
if (!compilerOptions?.baseUrl || !compilerOptions.paths) {
|
|
373
|
+
return undefined;
|
|
374
|
+
}
|
|
375
|
+
const normalizedPaths = {};
|
|
376
|
+
for (const [pattern, replacements] of Object.entries(compilerOptions.paths)) {
|
|
377
|
+
if (!replacements || replacements.length === 0) {
|
|
378
|
+
continue;
|
|
379
|
+
}
|
|
380
|
+
normalizedPaths[pattern] = Array.isArray(replacements)
|
|
381
|
+
? [...replacements]
|
|
382
|
+
: [replacements];
|
|
383
|
+
}
|
|
384
|
+
if (Object.keys(normalizedPaths).length === 0) {
|
|
385
|
+
return undefined;
|
|
386
|
+
}
|
|
387
|
+
const absoluteBaseUrl = node_path_1.default.isAbsolute(compilerOptions.baseUrl)
|
|
388
|
+
? compilerOptions.baseUrl
|
|
389
|
+
: node_path_1.default.resolve(configDir, compilerOptions.baseUrl);
|
|
390
|
+
return { absoluteBaseUrl, paths: normalizedPaths };
|
|
391
|
+
}
|
|
392
|
+
function resolveTsconfigPath(tsconfigPath, cwd) {
|
|
393
|
+
const absolute = node_path_1.default.isAbsolute(tsconfigPath)
|
|
394
|
+
? tsconfigPath
|
|
395
|
+
: node_path_1.default.resolve(cwd, tsconfigPath);
|
|
396
|
+
if (!(0, node_fs_1.existsSync)(absolute)) {
|
|
397
|
+
return undefined;
|
|
398
|
+
}
|
|
399
|
+
const stats = (0, node_fs_1.statSync)(absolute);
|
|
400
|
+
if (stats.isDirectory()) {
|
|
401
|
+
const candidate = node_path_1.default.join(absolute, 'tsconfig.json');
|
|
402
|
+
return (0, node_fs_1.existsSync)(candidate) ? candidate : undefined;
|
|
403
|
+
}
|
|
404
|
+
return absolute;
|
|
405
|
+
}
|
|
406
|
+
function findExistingFile(candidate, extensions) {
|
|
407
|
+
const candidateHasExt = hasExtension(candidate);
|
|
408
|
+
if (candidateHasExt && (0, node_fs_1.existsSync)(candidate)) {
|
|
409
|
+
return candidate;
|
|
410
|
+
}
|
|
411
|
+
if (!candidateHasExt) {
|
|
412
|
+
for (const ext of extensions) {
|
|
413
|
+
const withExt = `${candidate}${ext}`;
|
|
414
|
+
if ((0, node_fs_1.existsSync)(withExt)) {
|
|
415
|
+
return withExt;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
if ((0, node_fs_1.existsSync)(candidate) && (0, node_fs_1.statSync)(candidate).isDirectory()) {
|
|
420
|
+
for (const ext of extensions) {
|
|
421
|
+
const indexPath = node_path_1.default.join(candidate, `index${ext}`);
|
|
422
|
+
if ((0, node_fs_1.existsSync)(indexPath)) {
|
|
423
|
+
return indexPath;
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
return undefined;
|
|
428
|
+
}
|
|
429
|
+
function hasExtension(filePath) {
|
|
430
|
+
return Boolean(node_path_1.default.extname(filePath));
|
|
431
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { CssResolver } from './types.cjs';
|
|
2
|
+
export interface ModuleGraphOptions {
|
|
3
|
+
tsConfig?: string | Record<string, unknown>;
|
|
4
|
+
extensions?: string[];
|
|
5
|
+
conditions?: string[];
|
|
6
|
+
}
|
|
7
|
+
interface CollectOptions {
|
|
8
|
+
cwd: string;
|
|
9
|
+
styleExtensions: string[];
|
|
10
|
+
filter: (filePath: string) => boolean;
|
|
11
|
+
resolver?: CssResolver;
|
|
12
|
+
graphOptions?: ModuleGraphOptions;
|
|
13
|
+
}
|
|
14
|
+
export declare function collectStyleImports(entryPath: string, options: CollectOptions): Promise<string[]>;
|
|
15
|
+
export {};
|
|
@@ -24,8 +24,11 @@ function createSassImporter({ cwd, resolver, }) {
|
|
|
24
24
|
console.error('[knighted-css:sass] containing url:', context.containingUrl.href);
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
|
+
const containingPath = context?.containingUrl
|
|
28
|
+
? (0, node_url_1.fileURLToPath)(context.containingUrl)
|
|
29
|
+
: undefined;
|
|
27
30
|
if (shouldNormalizeSpecifier(url)) {
|
|
28
|
-
const resolvedPath = await resolveAliasSpecifier(url, resolver, cwd);
|
|
31
|
+
const resolvedPath = await resolveAliasSpecifier(url, resolver, cwd, containingPath);
|
|
29
32
|
if (!resolvedPath) {
|
|
30
33
|
if (debug) {
|
|
31
34
|
console.error('[knighted-css:sass] resolver returned no result for', url);
|
|
@@ -61,8 +64,8 @@ function createSassImporter({ cwd, resolver, }) {
|
|
|
61
64
|
},
|
|
62
65
|
};
|
|
63
66
|
}
|
|
64
|
-
async function resolveAliasSpecifier(specifier, resolver, cwd) {
|
|
65
|
-
const resolved = await resolver(specifier, { cwd });
|
|
67
|
+
async function resolveAliasSpecifier(specifier, resolver, cwd, from) {
|
|
68
|
+
const resolved = await resolver(specifier, { cwd, from });
|
|
66
69
|
if (!resolved) {
|
|
67
70
|
return undefined;
|
|
68
71
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
}) => string | Promise<string | undefined>;
|
|
1
|
+
import type { CssResolver } from './types.cjs';
|
|
2
|
+
export type { CssResolver } from './types.cjs';
|
|
4
3
|
export declare function createSassImporter({ cwd, resolver, }: {
|
|
5
4
|
cwd: string;
|
|
6
5
|
resolver?: CssResolver;
|
|
@@ -13,7 +12,7 @@ export declare function createSassImporter({ cwd, resolver, }: {
|
|
|
13
12
|
syntax: "scss" | "indented";
|
|
14
13
|
}>;
|
|
15
14
|
} | undefined;
|
|
16
|
-
export declare function resolveAliasSpecifier(specifier: string, resolver: CssResolver, cwd: string): Promise<string | undefined>;
|
|
15
|
+
export declare function resolveAliasSpecifier(specifier: string, resolver: CssResolver, cwd: string, from?: string): Promise<string | undefined>;
|
|
17
16
|
export declare function shouldNormalizeSpecifier(specifier: string): boolean;
|
|
18
17
|
export declare function ensureSassPath(filePath: string): string | undefined;
|
|
19
18
|
export declare function resolveRelativeSpecifier(specifier: string, containingUrl?: URL | null): string | undefined;
|
package/dist/css.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import type { Options as DependencyTreeOpts } from 'dependency-tree';
|
|
2
1
|
import { type TransformOptions as LightningTransformOptions } from 'lightningcss';
|
|
3
2
|
import { type SpecificitySelector, type SpecificityStrategy } from './helpers.js';
|
|
4
|
-
import type {
|
|
5
|
-
|
|
3
|
+
import type { ModuleGraphOptions } from './moduleGraph.js';
|
|
4
|
+
import type { CssResolver } from './types.js';
|
|
5
|
+
export type { CssResolver } from './types.js';
|
|
6
|
+
export type { ModuleGraphOptions } from './moduleGraph.js';
|
|
6
7
|
export declare const DEFAULT_EXTENSIONS: string[];
|
|
7
8
|
type LightningCssConfig = boolean | Partial<Omit<LightningTransformOptions<never>, 'code'>>;
|
|
8
9
|
type PeerLoader = (name: string) => Promise<unknown>;
|
|
@@ -16,7 +17,7 @@ export interface CssOptions {
|
|
|
16
17
|
strategy?: SpecificityStrategy;
|
|
17
18
|
match?: SpecificitySelector[];
|
|
18
19
|
};
|
|
19
|
-
|
|
20
|
+
moduleGraph?: ModuleGraphOptions;
|
|
20
21
|
resolver?: CssResolver;
|
|
21
22
|
peerResolver?: PeerLoader;
|
|
22
23
|
}
|
package/dist/css.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import { existsSync, promises as fs } from 'node:fs';
|
|
3
|
-
import dependencyTree from 'dependency-tree';
|
|
4
3
|
import { composeVisitors, transform as lightningTransform, } from 'lightningcss';
|
|
5
4
|
import { applyStringSpecificityBoost, buildSpecificityVisitor, } from './helpers.js';
|
|
5
|
+
import { collectStyleImports } from './moduleGraph.js';
|
|
6
6
|
import { createSassImporter } from './sassInternals.js';
|
|
7
7
|
export const DEFAULT_EXTENSIONS = ['.css', '.scss', '.sass', '.less', '.css.ts'];
|
|
8
8
|
export async function css(entry, options = {}) {
|
|
@@ -13,11 +13,12 @@ export async function cssWithMeta(entry, options = {}) {
|
|
|
13
13
|
const cwd = options.cwd ? path.resolve(options.cwd) : process.cwd();
|
|
14
14
|
const entryPath = await resolveEntry(entry, cwd, options.resolver);
|
|
15
15
|
const extensions = (options.extensions ?? DEFAULT_EXTENSIONS).map(ext => ext.toLowerCase());
|
|
16
|
-
const files = collectStyleDependencies(entryPath, {
|
|
16
|
+
const files = await collectStyleDependencies(entryPath, {
|
|
17
17
|
cwd,
|
|
18
18
|
extensions,
|
|
19
19
|
filter: options.filter,
|
|
20
|
-
|
|
20
|
+
graphOptions: options.moduleGraph,
|
|
21
|
+
resolver: options.resolver,
|
|
21
22
|
});
|
|
22
23
|
if (files.length === 0) {
|
|
23
24
|
return { css: '', files: [] };
|
|
@@ -67,30 +68,33 @@ async function resolveEntry(entry, cwd, resolver) {
|
|
|
67
68
|
}
|
|
68
69
|
return path.resolve(cwd, entry);
|
|
69
70
|
}
|
|
70
|
-
function collectStyleDependencies(entryPath, { cwd, extensions, filter,
|
|
71
|
+
async function collectStyleDependencies(entryPath, { cwd, extensions, filter, graphOptions, resolver, }) {
|
|
71
72
|
const seen = new Set();
|
|
72
73
|
const order = [];
|
|
73
74
|
const shouldInclude = typeof filter === 'function'
|
|
74
75
|
? filter
|
|
75
76
|
: (filePath) => !filePath.includes('node_modules');
|
|
76
77
|
const entryIsStyle = Boolean(matchExtension(entryPath, extensions));
|
|
77
|
-
let
|
|
78
|
+
let discoveredStyles = [];
|
|
78
79
|
if (!entryIsStyle) {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
directory: cwd,
|
|
80
|
+
discoveredStyles = await collectStyleImports(entryPath, {
|
|
81
|
+
cwd,
|
|
82
|
+
styleExtensions: extensions,
|
|
83
83
|
filter: shouldInclude,
|
|
84
|
-
|
|
85
|
-
|
|
84
|
+
resolver,
|
|
85
|
+
graphOptions,
|
|
86
|
+
});
|
|
86
87
|
}
|
|
87
|
-
const candidates = entryIsStyle ? [entryPath] : [entryPath, ...
|
|
88
|
+
const candidates = entryIsStyle ? [entryPath] : [entryPath, ...discoveredStyles];
|
|
88
89
|
for (const candidate of candidates) {
|
|
89
90
|
const match = matchExtension(candidate, extensions);
|
|
90
|
-
if (!match
|
|
91
|
+
if (!match)
|
|
92
|
+
continue;
|
|
93
|
+
const resolvedCandidate = path.resolve(candidate);
|
|
94
|
+
if (seen.has(resolvedCandidate))
|
|
91
95
|
continue;
|
|
92
|
-
seen.add(
|
|
93
|
-
order.push({ path:
|
|
96
|
+
seen.add(resolvedCandidate);
|
|
97
|
+
order.push({ path: resolvedCandidate, ext: match });
|
|
94
98
|
}
|
|
95
99
|
return order;
|
|
96
100
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { CssResolver } from './types.js';
|
|
2
|
+
export interface ModuleGraphOptions {
|
|
3
|
+
tsConfig?: string | Record<string, unknown>;
|
|
4
|
+
extensions?: string[];
|
|
5
|
+
conditions?: string[];
|
|
6
|
+
}
|
|
7
|
+
interface CollectOptions {
|
|
8
|
+
cwd: string;
|
|
9
|
+
styleExtensions: string[];
|
|
10
|
+
filter: (filePath: string) => boolean;
|
|
11
|
+
resolver?: CssResolver;
|
|
12
|
+
graphOptions?: ModuleGraphOptions;
|
|
13
|
+
}
|
|
14
|
+
export declare function collectStyleImports(entryPath: string, options: CollectOptions): Promise<string[]>;
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { builtinModules } from 'node:module';
|
|
3
|
+
import { existsSync, promises as fs, statSync } from 'node:fs';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import { parseSync, Visitor } from 'oxc-parser';
|
|
6
|
+
import { ResolverFactory, } from 'oxc-resolver';
|
|
7
|
+
import { createMatchPath } from 'tsconfig-paths';
|
|
8
|
+
import { getTsconfig } from 'get-tsconfig';
|
|
9
|
+
const SCRIPT_EXTENSIONS = ['.ts', '.tsx', '.mts', '.cts', '.js', '.jsx', '.mjs', '.cjs'];
|
|
10
|
+
const BUILTIN_SPECIFIERS = new Set([
|
|
11
|
+
...builtinModules,
|
|
12
|
+
...builtinModules.map(mod => `node:${mod}`),
|
|
13
|
+
]);
|
|
14
|
+
const tsconfigResultCache = new Map();
|
|
15
|
+
const tsconfigFsCache = new Map();
|
|
16
|
+
export async function collectStyleImports(entryPath, options) {
|
|
17
|
+
const { cwd, styleExtensions, filter, resolver, graphOptions } = options;
|
|
18
|
+
const normalizedStyles = normalizeExtensions(styleExtensions);
|
|
19
|
+
const scriptExtensions = normalizeExtensions([
|
|
20
|
+
...SCRIPT_EXTENSIONS,
|
|
21
|
+
...(graphOptions?.extensions ?? []),
|
|
22
|
+
]);
|
|
23
|
+
const resolutionExtensions = dedupeExtensions([
|
|
24
|
+
...scriptExtensions,
|
|
25
|
+
...normalizedStyles,
|
|
26
|
+
]);
|
|
27
|
+
const tsconfigMatcher = createTsconfigMatcher(graphOptions?.tsConfig, cwd, resolutionExtensions);
|
|
28
|
+
const seenScripts = new Set();
|
|
29
|
+
const seenStyles = new Set();
|
|
30
|
+
const styleOrder = [];
|
|
31
|
+
const resolutionCache = new Map();
|
|
32
|
+
const resolverFactory = createResolverFactory(cwd, resolutionExtensions, scriptExtensions, graphOptions);
|
|
33
|
+
async function walk(filePath) {
|
|
34
|
+
const absolutePath = path.resolve(filePath);
|
|
35
|
+
if (seenScripts.has(absolutePath)) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
seenScripts.add(absolutePath);
|
|
39
|
+
const source = await readSourceFile(absolutePath);
|
|
40
|
+
if (!source) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
const specifiers = extractModuleSpecifiers(source, absolutePath);
|
|
44
|
+
for (const specifier of specifiers) {
|
|
45
|
+
if (!specifier || isBuiltinSpecifier(specifier)) {
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
const resolved = await resolveImport(specifier, absolutePath);
|
|
49
|
+
if (!resolved) {
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
const normalized = path.resolve(resolved);
|
|
53
|
+
if (!filter(normalized)) {
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
if (isStyleExtension(normalized, normalizedStyles)) {
|
|
57
|
+
if (!seenStyles.has(normalized)) {
|
|
58
|
+
seenStyles.add(normalized);
|
|
59
|
+
styleOrder.push(normalized);
|
|
60
|
+
}
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
if (isScriptExtension(normalized, scriptExtensions)) {
|
|
64
|
+
await walk(normalized);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
async function resolveImport(specifier, importer) {
|
|
69
|
+
const cacheKey = `${importer}::${specifier}`;
|
|
70
|
+
if (resolutionCache.has(cacheKey)) {
|
|
71
|
+
return resolutionCache.get(cacheKey);
|
|
72
|
+
}
|
|
73
|
+
let resolved;
|
|
74
|
+
if (resolver) {
|
|
75
|
+
resolved = normalizeResolverResult(await resolver(specifier, { cwd, from: importer }), cwd);
|
|
76
|
+
}
|
|
77
|
+
if (!resolved && tsconfigMatcher) {
|
|
78
|
+
resolved = tsconfigMatcher(specifier);
|
|
79
|
+
}
|
|
80
|
+
if (!resolved) {
|
|
81
|
+
resolved = resolveWithFactory(resolverFactory, specifier, importer, resolutionExtensions);
|
|
82
|
+
}
|
|
83
|
+
resolutionCache.set(cacheKey, resolved);
|
|
84
|
+
return resolved;
|
|
85
|
+
}
|
|
86
|
+
await walk(entryPath);
|
|
87
|
+
return styleOrder;
|
|
88
|
+
}
|
|
89
|
+
function normalizeExtensions(extensions) {
|
|
90
|
+
const result = new Set();
|
|
91
|
+
for (const ext of extensions) {
|
|
92
|
+
if (!ext)
|
|
93
|
+
continue;
|
|
94
|
+
const normalized = ext.startsWith('.') ? ext.toLowerCase() : `.${ext.toLowerCase()}`;
|
|
95
|
+
result.add(normalized);
|
|
96
|
+
}
|
|
97
|
+
return Array.from(result);
|
|
98
|
+
}
|
|
99
|
+
function dedupeExtensions(extensions) {
|
|
100
|
+
const result = new Set();
|
|
101
|
+
for (const ext of extensions) {
|
|
102
|
+
result.add(ext);
|
|
103
|
+
}
|
|
104
|
+
return Array.from(result);
|
|
105
|
+
}
|
|
106
|
+
function isBuiltinSpecifier(specifier) {
|
|
107
|
+
if (BUILTIN_SPECIFIERS.has(specifier)) {
|
|
108
|
+
return true;
|
|
109
|
+
}
|
|
110
|
+
if (specifier.startsWith('node:')) {
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
function isStyleExtension(filePath, extensions) {
|
|
116
|
+
const lower = filePath.toLowerCase();
|
|
117
|
+
return extensions.some(ext => lower.endsWith(ext));
|
|
118
|
+
}
|
|
119
|
+
function isScriptExtension(filePath, extensions) {
|
|
120
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
121
|
+
return extensions.includes(ext);
|
|
122
|
+
}
|
|
123
|
+
async function readSourceFile(filePath) {
|
|
124
|
+
try {
|
|
125
|
+
return await fs.readFile(filePath, 'utf8');
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
return undefined;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
function extractModuleSpecifiers(sourceText, filePath) {
|
|
132
|
+
let program;
|
|
133
|
+
try {
|
|
134
|
+
;
|
|
135
|
+
({ program } = parseSync(filePath, sourceText, { sourceType: 'unambiguous' }));
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
return [];
|
|
139
|
+
}
|
|
140
|
+
const specifiers = [];
|
|
141
|
+
const addSpecifier = (raw) => {
|
|
142
|
+
if (!raw) {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
const normalized = normalizeSpecifier(raw);
|
|
146
|
+
if (normalized) {
|
|
147
|
+
specifiers.push(normalized);
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
const visitor = new Visitor({
|
|
151
|
+
ImportDeclaration(node) {
|
|
152
|
+
addSpecifier(node.source?.value);
|
|
153
|
+
},
|
|
154
|
+
ExportNamedDeclaration(node) {
|
|
155
|
+
if (node.source) {
|
|
156
|
+
addSpecifier(node.source.value);
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
ExportAllDeclaration(node) {
|
|
160
|
+
addSpecifier(node.source?.value);
|
|
161
|
+
},
|
|
162
|
+
TSImportEqualsDeclaration(node) {
|
|
163
|
+
const specifier = extractImportEqualsSpecifier(node);
|
|
164
|
+
if (specifier) {
|
|
165
|
+
addSpecifier(specifier);
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
ImportExpression(node) {
|
|
169
|
+
const specifier = getStringFromExpression(node.source);
|
|
170
|
+
if (specifier) {
|
|
171
|
+
addSpecifier(specifier);
|
|
172
|
+
}
|
|
173
|
+
},
|
|
174
|
+
CallExpression(node) {
|
|
175
|
+
if (!isRequireLikeCallee(node.callee)) {
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
const specifier = getStringFromArgument(node.arguments[0]);
|
|
179
|
+
if (specifier) {
|
|
180
|
+
addSpecifier(specifier);
|
|
181
|
+
}
|
|
182
|
+
},
|
|
183
|
+
});
|
|
184
|
+
visitor.visit(program);
|
|
185
|
+
return specifiers;
|
|
186
|
+
}
|
|
187
|
+
function normalizeSpecifier(raw) {
|
|
188
|
+
if (!raw)
|
|
189
|
+
return '';
|
|
190
|
+
const trimmed = raw.trim();
|
|
191
|
+
if (!trimmed || trimmed.startsWith('\0')) {
|
|
192
|
+
return '';
|
|
193
|
+
}
|
|
194
|
+
const queryIndex = trimmed.search(/[?#]/);
|
|
195
|
+
const withoutQuery = queryIndex === -1 ? trimmed : trimmed.slice(0, queryIndex);
|
|
196
|
+
if (!withoutQuery) {
|
|
197
|
+
return '';
|
|
198
|
+
}
|
|
199
|
+
if (/^[a-z][\w+.-]*:/i.test(withoutQuery) && !withoutQuery.startsWith('file:')) {
|
|
200
|
+
return '';
|
|
201
|
+
}
|
|
202
|
+
return withoutQuery;
|
|
203
|
+
}
|
|
204
|
+
function extractImportEqualsSpecifier(node) {
|
|
205
|
+
if (node.moduleReference.type === 'TSExternalModuleReference') {
|
|
206
|
+
return node.moduleReference.expression.value;
|
|
207
|
+
}
|
|
208
|
+
return undefined;
|
|
209
|
+
}
|
|
210
|
+
function getStringFromArgument(argument) {
|
|
211
|
+
if (!argument || argument.type === 'SpreadElement') {
|
|
212
|
+
return undefined;
|
|
213
|
+
}
|
|
214
|
+
return getStringFromExpression(argument);
|
|
215
|
+
}
|
|
216
|
+
function getStringFromExpression(expression) {
|
|
217
|
+
if (!expression) {
|
|
218
|
+
return undefined;
|
|
219
|
+
}
|
|
220
|
+
if (expression.type === 'Literal') {
|
|
221
|
+
const literalValue = expression.value;
|
|
222
|
+
return typeof literalValue === 'string' ? literalValue : undefined;
|
|
223
|
+
}
|
|
224
|
+
if (expression.type === 'TemplateLiteral' && expression.expressions.length === 0) {
|
|
225
|
+
const [first] = expression.quasis;
|
|
226
|
+
return first?.value.cooked ?? first?.value.raw ?? undefined;
|
|
227
|
+
}
|
|
228
|
+
return undefined;
|
|
229
|
+
}
|
|
230
|
+
function isRequireLikeCallee(expression) {
|
|
231
|
+
const target = unwrapExpression(expression);
|
|
232
|
+
if (target.type === 'Identifier') {
|
|
233
|
+
return target.name === 'require';
|
|
234
|
+
}
|
|
235
|
+
if (target.type === 'MemberExpression') {
|
|
236
|
+
const object = target.object;
|
|
237
|
+
if (object.type === 'Identifier') {
|
|
238
|
+
return object.name === 'require';
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
return false;
|
|
242
|
+
}
|
|
243
|
+
function unwrapExpression(expression) {
|
|
244
|
+
if (expression.type === 'ChainExpression') {
|
|
245
|
+
const inner = expression.expression;
|
|
246
|
+
if (inner.type === 'CallExpression') {
|
|
247
|
+
return unwrapExpression(inner.callee);
|
|
248
|
+
}
|
|
249
|
+
return unwrapExpression(inner);
|
|
250
|
+
}
|
|
251
|
+
if (expression.type === 'TSNonNullExpression') {
|
|
252
|
+
return unwrapExpression(expression.expression);
|
|
253
|
+
}
|
|
254
|
+
return expression;
|
|
255
|
+
}
|
|
256
|
+
function normalizeResolverResult(result, cwd) {
|
|
257
|
+
if (!result) {
|
|
258
|
+
return undefined;
|
|
259
|
+
}
|
|
260
|
+
if (result.startsWith('file://')) {
|
|
261
|
+
try {
|
|
262
|
+
return fileURLToPath(new URL(result));
|
|
263
|
+
}
|
|
264
|
+
catch {
|
|
265
|
+
return undefined;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
return path.isAbsolute(result) ? result : path.resolve(cwd, result);
|
|
269
|
+
}
|
|
270
|
+
function resolveWithFactory(factory, specifier, importer, extensions) {
|
|
271
|
+
if (specifier.startsWith('file://')) {
|
|
272
|
+
try {
|
|
273
|
+
return findExistingFile(fileURLToPath(new URL(specifier)), extensions);
|
|
274
|
+
}
|
|
275
|
+
catch {
|
|
276
|
+
return undefined;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
if (/^[a-z][\w+.-]*:/i.test(specifier)) {
|
|
280
|
+
return undefined;
|
|
281
|
+
}
|
|
282
|
+
try {
|
|
283
|
+
const result = factory.resolveFileSync(importer, specifier);
|
|
284
|
+
return result?.path;
|
|
285
|
+
}
|
|
286
|
+
catch {
|
|
287
|
+
return undefined;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
function createResolverFactory(cwd, extensions, scriptExtensions, graphOptions) {
|
|
291
|
+
const options = {
|
|
292
|
+
extensions,
|
|
293
|
+
conditionNames: graphOptions?.conditions,
|
|
294
|
+
};
|
|
295
|
+
const extensionAlias = buildExtensionAlias(scriptExtensions);
|
|
296
|
+
if (extensionAlias) {
|
|
297
|
+
options.extensionAlias = extensionAlias;
|
|
298
|
+
}
|
|
299
|
+
const tsconfigOption = resolveResolverTsconfig(graphOptions?.tsConfig, cwd);
|
|
300
|
+
if (tsconfigOption) {
|
|
301
|
+
options.tsconfig = tsconfigOption;
|
|
302
|
+
}
|
|
303
|
+
return new ResolverFactory(options);
|
|
304
|
+
}
|
|
305
|
+
function buildExtensionAlias(scriptExtensions) {
|
|
306
|
+
const alias = {};
|
|
307
|
+
const jsTargets = dedupeExtensions(scriptExtensions.filter(ext => ['.js', '.ts', '.tsx', '.mjs', '.cjs', '.mts', '.cts'].includes(ext)));
|
|
308
|
+
if (jsTargets.length > 0) {
|
|
309
|
+
for (const key of ['.js', '.mjs', '.cjs']) {
|
|
310
|
+
alias[key] = jsTargets;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
const jsxTargets = dedupeExtensions(scriptExtensions.filter(ext => ext === '.jsx' || ext === '.tsx'));
|
|
314
|
+
if (jsxTargets.length > 0) {
|
|
315
|
+
alias['.jsx'] = jsxTargets;
|
|
316
|
+
}
|
|
317
|
+
return Object.keys(alias).length > 0 ? alias : undefined;
|
|
318
|
+
}
|
|
319
|
+
function resolveResolverTsconfig(input, cwd) {
|
|
320
|
+
if (!input || typeof input !== 'string') {
|
|
321
|
+
return undefined;
|
|
322
|
+
}
|
|
323
|
+
const resolved = resolveTsconfigPath(input, cwd);
|
|
324
|
+
if (!resolved) {
|
|
325
|
+
return undefined;
|
|
326
|
+
}
|
|
327
|
+
return { configFile: resolved };
|
|
328
|
+
}
|
|
329
|
+
function createTsconfigMatcher(input, cwd, extensions) {
|
|
330
|
+
const config = loadTsconfigPaths(input, cwd);
|
|
331
|
+
if (!config) {
|
|
332
|
+
return undefined;
|
|
333
|
+
}
|
|
334
|
+
const matchPath = createMatchPath(config.absoluteBaseUrl, config.paths);
|
|
335
|
+
return (specifier) => {
|
|
336
|
+
const matched = matchPath(specifier, undefined, undefined, extensions);
|
|
337
|
+
if (!matched) {
|
|
338
|
+
return undefined;
|
|
339
|
+
}
|
|
340
|
+
return findExistingFile(matched, extensions) ?? matched;
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
function loadTsconfigPaths(input, cwd) {
|
|
344
|
+
if (!input) {
|
|
345
|
+
return undefined;
|
|
346
|
+
}
|
|
347
|
+
if (typeof input === 'string') {
|
|
348
|
+
const target = path.isAbsolute(input) ? input : path.resolve(cwd, input);
|
|
349
|
+
const cached = tsconfigResultCache.get(target);
|
|
350
|
+
if (cached !== undefined) {
|
|
351
|
+
return cached ?? undefined;
|
|
352
|
+
}
|
|
353
|
+
const result = getTsconfig(target, undefined, tsconfigFsCache);
|
|
354
|
+
if (!result) {
|
|
355
|
+
tsconfigResultCache.set(target, null);
|
|
356
|
+
return undefined;
|
|
357
|
+
}
|
|
358
|
+
const normalized = normalizeTsconfigCompilerOptions(result.config.compilerOptions, path.dirname(result.path));
|
|
359
|
+
tsconfigResultCache.set(target, normalized ?? null);
|
|
360
|
+
return normalized;
|
|
361
|
+
}
|
|
362
|
+
const compilerOptions = input.compilerOptions;
|
|
363
|
+
return normalizeTsconfigCompilerOptions(compilerOptions, cwd);
|
|
364
|
+
}
|
|
365
|
+
function normalizeTsconfigCompilerOptions(compilerOptions, configDir) {
|
|
366
|
+
if (!compilerOptions?.baseUrl || !compilerOptions.paths) {
|
|
367
|
+
return undefined;
|
|
368
|
+
}
|
|
369
|
+
const normalizedPaths = {};
|
|
370
|
+
for (const [pattern, replacements] of Object.entries(compilerOptions.paths)) {
|
|
371
|
+
if (!replacements || replacements.length === 0) {
|
|
372
|
+
continue;
|
|
373
|
+
}
|
|
374
|
+
normalizedPaths[pattern] = Array.isArray(replacements)
|
|
375
|
+
? [...replacements]
|
|
376
|
+
: [replacements];
|
|
377
|
+
}
|
|
378
|
+
if (Object.keys(normalizedPaths).length === 0) {
|
|
379
|
+
return undefined;
|
|
380
|
+
}
|
|
381
|
+
const absoluteBaseUrl = path.isAbsolute(compilerOptions.baseUrl)
|
|
382
|
+
? compilerOptions.baseUrl
|
|
383
|
+
: path.resolve(configDir, compilerOptions.baseUrl);
|
|
384
|
+
return { absoluteBaseUrl, paths: normalizedPaths };
|
|
385
|
+
}
|
|
386
|
+
function resolveTsconfigPath(tsconfigPath, cwd) {
|
|
387
|
+
const absolute = path.isAbsolute(tsconfigPath)
|
|
388
|
+
? tsconfigPath
|
|
389
|
+
: path.resolve(cwd, tsconfigPath);
|
|
390
|
+
if (!existsSync(absolute)) {
|
|
391
|
+
return undefined;
|
|
392
|
+
}
|
|
393
|
+
const stats = statSync(absolute);
|
|
394
|
+
if (stats.isDirectory()) {
|
|
395
|
+
const candidate = path.join(absolute, 'tsconfig.json');
|
|
396
|
+
return existsSync(candidate) ? candidate : undefined;
|
|
397
|
+
}
|
|
398
|
+
return absolute;
|
|
399
|
+
}
|
|
400
|
+
function findExistingFile(candidate, extensions) {
|
|
401
|
+
const candidateHasExt = hasExtension(candidate);
|
|
402
|
+
if (candidateHasExt && existsSync(candidate)) {
|
|
403
|
+
return candidate;
|
|
404
|
+
}
|
|
405
|
+
if (!candidateHasExt) {
|
|
406
|
+
for (const ext of extensions) {
|
|
407
|
+
const withExt = `${candidate}${ext}`;
|
|
408
|
+
if (existsSync(withExt)) {
|
|
409
|
+
return withExt;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
if (existsSync(candidate) && statSync(candidate).isDirectory()) {
|
|
414
|
+
for (const ext of extensions) {
|
|
415
|
+
const indexPath = path.join(candidate, `index${ext}`);
|
|
416
|
+
if (existsSync(indexPath)) {
|
|
417
|
+
return indexPath;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
return undefined;
|
|
422
|
+
}
|
|
423
|
+
function hasExtension(filePath) {
|
|
424
|
+
return Boolean(path.extname(filePath));
|
|
425
|
+
}
|
package/dist/sassInternals.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
}) => string | Promise<string | undefined>;
|
|
1
|
+
import type { CssResolver } from './types.js';
|
|
2
|
+
export type { CssResolver } from './types.js';
|
|
4
3
|
export declare function createSassImporter({ cwd, resolver, }: {
|
|
5
4
|
cwd: string;
|
|
6
5
|
resolver?: CssResolver;
|
|
@@ -13,7 +12,7 @@ export declare function createSassImporter({ cwd, resolver, }: {
|
|
|
13
12
|
syntax: "scss" | "indented";
|
|
14
13
|
}>;
|
|
15
14
|
} | undefined;
|
|
16
|
-
export declare function resolveAliasSpecifier(specifier: string, resolver: CssResolver, cwd: string): Promise<string | undefined>;
|
|
15
|
+
export declare function resolveAliasSpecifier(specifier: string, resolver: CssResolver, cwd: string, from?: string): Promise<string | undefined>;
|
|
17
16
|
export declare function shouldNormalizeSpecifier(specifier: string): boolean;
|
|
18
17
|
export declare function ensureSassPath(filePath: string): string | undefined;
|
|
19
18
|
export declare function resolveRelativeSpecifier(specifier: string, containingUrl?: URL | null): string | undefined;
|
package/dist/sassInternals.js
CHANGED
|
@@ -13,8 +13,11 @@ export function createSassImporter({ cwd, resolver, }) {
|
|
|
13
13
|
console.error('[knighted-css:sass] containing url:', context.containingUrl.href);
|
|
14
14
|
}
|
|
15
15
|
}
|
|
16
|
+
const containingPath = context?.containingUrl
|
|
17
|
+
? fileURLToPath(context.containingUrl)
|
|
18
|
+
: undefined;
|
|
16
19
|
if (shouldNormalizeSpecifier(url)) {
|
|
17
|
-
const resolvedPath = await resolveAliasSpecifier(url, resolver, cwd);
|
|
20
|
+
const resolvedPath = await resolveAliasSpecifier(url, resolver, cwd, containingPath);
|
|
18
21
|
if (!resolvedPath) {
|
|
19
22
|
if (debug) {
|
|
20
23
|
console.error('[knighted-css:sass] resolver returned no result for', url);
|
|
@@ -50,8 +53,8 @@ export function createSassImporter({ cwd, resolver, }) {
|
|
|
50
53
|
},
|
|
51
54
|
};
|
|
52
55
|
}
|
|
53
|
-
export async function resolveAliasSpecifier(specifier, resolver, cwd) {
|
|
54
|
-
const resolved = await resolver(specifier, { cwd });
|
|
56
|
+
export async function resolveAliasSpecifier(specifier, resolver, cwd, from) {
|
|
57
|
+
const resolved = await resolver(specifier, { cwd, from });
|
|
55
58
|
if (!resolved) {
|
|
56
59
|
return undefined;
|
|
57
60
|
}
|
package/dist/types.d.ts
ADDED
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@knighted/css",
|
|
3
|
-
"version": "1.0.0-rc.
|
|
3
|
+
"version": "1.0.0-rc.9",
|
|
4
4
|
"description": "A build-time utility that traverses JavaScript/TypeScript module dependency graphs to extract, compile, and optimize all imported CSS into a single, in-memory string.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/css.js",
|
|
@@ -76,15 +76,13 @@
|
|
|
76
76
|
"prepack": "npm run build"
|
|
77
77
|
},
|
|
78
78
|
"dependencies": {
|
|
79
|
-
"dependency-tree": "^11.2.0",
|
|
80
79
|
"es-module-lexer": "^2.0.0",
|
|
81
80
|
"lightningcss": "^1.30.2",
|
|
82
|
-
"node-module-type": "^1.0.1"
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
"
|
|
86
|
-
|
|
87
|
-
}
|
|
81
|
+
"node-module-type": "^1.0.1",
|
|
82
|
+
"get-tsconfig": "^4.13.0",
|
|
83
|
+
"oxc-parser": "^0.104.0",
|
|
84
|
+
"oxc-resolver": "^11.16.0",
|
|
85
|
+
"tsconfig-paths": "^4.2.0"
|
|
88
86
|
},
|
|
89
87
|
"peerDependencies": {
|
|
90
88
|
"@vanilla-extract/integration": "^8.0.0",
|