@knighted/css 1.0.0-rc.10 → 1.0.0-rc.12
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/generateTypes.cjs +104 -136
- package/dist/cjs/generateTypes.d.cts +23 -24
- package/dist/generateTypes.d.ts +23 -24
- package/dist/generateTypes.js +104 -136
- package/package.json +1 -1
|
@@ -16,7 +16,6 @@ const node_module_type_1 = require("node-module-type");
|
|
|
16
16
|
const get_tsconfig_1 = require("get-tsconfig");
|
|
17
17
|
const tsconfig_paths_1 = require("tsconfig-paths");
|
|
18
18
|
const css_js_1 = require("./css.cjs");
|
|
19
|
-
const loaderInternals_js_1 = require("./loaderInternals.cjs");
|
|
20
19
|
const stableSelectorsLiteral_js_1 = require("./stableSelectorsLiteral.cjs");
|
|
21
20
|
const stableNamespace_js_1 = require("./stableNamespace.cjs");
|
|
22
21
|
let activeCssWithMeta = css_js_1.cssWithMeta;
|
|
@@ -65,22 +64,19 @@ function getImportMetaUrl() {
|
|
|
65
64
|
}
|
|
66
65
|
}
|
|
67
66
|
const PACKAGE_ROOT = resolvePackageRoot();
|
|
68
|
-
const
|
|
69
|
-
const
|
|
67
|
+
const SELECTOR_REFERENCE = '.knighted-css';
|
|
68
|
+
const SELECTOR_MODULE_SUFFIX = '.knighted-css.ts';
|
|
70
69
|
async function generateTypes(options = {}) {
|
|
71
70
|
const rootDir = node_path_1.default.resolve(options.rootDir ?? process.cwd());
|
|
72
71
|
const include = normalizeIncludeOptions(options.include, rootDir);
|
|
73
|
-
const
|
|
74
|
-
const typesRoot = node_path_1.default.resolve(options.typesRoot ?? DEFAULT_TYPES_ROOT);
|
|
72
|
+
const cacheDir = node_path_1.default.resolve(options.outDir ?? node_path_1.default.join(rootDir, '.knighted-css'));
|
|
75
73
|
const tsconfig = loadTsconfigResolutionContext(rootDir);
|
|
76
74
|
await es_module_lexer_1.init;
|
|
77
|
-
await promises_1.default.mkdir(
|
|
78
|
-
await promises_1.default.mkdir(typesRoot, { recursive: true });
|
|
75
|
+
await promises_1.default.mkdir(cacheDir, { recursive: true });
|
|
79
76
|
const internalOptions = {
|
|
80
77
|
rootDir,
|
|
81
78
|
include,
|
|
82
|
-
|
|
83
|
-
typesRoot,
|
|
79
|
+
cacheDir,
|
|
84
80
|
stableNamespace: options.stableNamespace,
|
|
85
81
|
tsconfig,
|
|
86
82
|
};
|
|
@@ -89,32 +85,27 @@ async function generateTypes(options = {}) {
|
|
|
89
85
|
async function generateDeclarations(options) {
|
|
90
86
|
const peerResolver = createProjectPeerResolver(options.rootDir);
|
|
91
87
|
const files = await collectCandidateFiles(options.include);
|
|
92
|
-
const
|
|
93
|
-
const
|
|
94
|
-
const
|
|
88
|
+
const selectorModulesManifestPath = node_path_1.default.join(options.cacheDir, 'selector-modules.json');
|
|
89
|
+
const previousSelectorManifest = await readManifest(selectorModulesManifestPath);
|
|
90
|
+
const nextSelectorManifest = {};
|
|
95
91
|
const selectorCache = new Map();
|
|
96
|
-
const
|
|
97
|
-
const declarations = [];
|
|
92
|
+
const processedSelectors = new Set();
|
|
98
93
|
const warnings = [];
|
|
99
|
-
let
|
|
94
|
+
let selectorModuleWrites = 0;
|
|
100
95
|
for (const filePath of files) {
|
|
101
96
|
const matches = await findSpecifierImports(filePath);
|
|
102
97
|
for (const match of matches) {
|
|
103
98
|
const cleaned = match.specifier.trim();
|
|
104
99
|
const inlineFree = stripInlineLoader(cleaned);
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
if (!query || !(0, loaderInternals_js_1.hasQueryFlag)(query, loaderInternals_js_1.TYPES_QUERY_FLAG)) {
|
|
109
|
-
continue;
|
|
110
|
-
}
|
|
111
|
-
if (processedSpecifiers.has(cleaned)) {
|
|
100
|
+
const { resource } = splitResourceAndQuery(inlineFree);
|
|
101
|
+
const selectorSource = extractSelectorSourceSpecifier(resource);
|
|
102
|
+
if (!selectorSource) {
|
|
112
103
|
continue;
|
|
113
104
|
}
|
|
114
105
|
const resolvedNamespace = (0, stableNamespace_js_1.resolveStableNamespace)(options.stableNamespace);
|
|
115
|
-
const resolvedPath = await resolveImportPath(
|
|
106
|
+
const resolvedPath = await resolveImportPath(selectorSource, match.importer, options.rootDir, options.tsconfig);
|
|
116
107
|
if (!resolvedPath) {
|
|
117
|
-
warnings.push(`Unable to resolve ${
|
|
108
|
+
warnings.push(`Unable to resolve ${selectorSource} referenced by ${relativeToRoot(match.importer, options.rootDir)}.`);
|
|
118
109
|
continue;
|
|
119
110
|
}
|
|
120
111
|
const cacheKey = `${resolvedPath}::${resolvedNamespace}`;
|
|
@@ -138,38 +129,28 @@ async function generateDeclarations(options) {
|
|
|
138
129
|
}
|
|
139
130
|
selectorCache.set(cacheKey, selectorMap);
|
|
140
131
|
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
const
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
if (needsWrite) {
|
|
149
|
-
await promises_1.default.writeFile(targetPath, declaration, 'utf8');
|
|
150
|
-
writes += 1;
|
|
132
|
+
if (!isWithinRoot(resolvedPath, options.rootDir)) {
|
|
133
|
+
warnings.push(`Skipping selector module for ${relativeToRoot(resolvedPath, options.rootDir)} because it is outside the project root.`);
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
const manifestKey = buildSelectorModuleManifestKey(resolvedPath);
|
|
137
|
+
if (processedSelectors.has(manifestKey)) {
|
|
138
|
+
continue;
|
|
151
139
|
}
|
|
152
|
-
|
|
153
|
-
if (
|
|
154
|
-
|
|
140
|
+
const moduleWrite = await ensureSelectorModule(resolvedPath, selectorMap, previousSelectorManifest, nextSelectorManifest);
|
|
141
|
+
if (moduleWrite) {
|
|
142
|
+
selectorModuleWrites += 1;
|
|
155
143
|
}
|
|
156
|
-
|
|
144
|
+
processedSelectors.add(manifestKey);
|
|
157
145
|
}
|
|
158
146
|
}
|
|
159
|
-
const
|
|
160
|
-
await writeManifest(
|
|
161
|
-
const typesIndexPath = node_path_1.default.join(options.typesRoot, 'index.d.ts');
|
|
162
|
-
await writeTypesIndex(typesIndexPath, nextManifest, options.outDir);
|
|
163
|
-
if (Object.keys(nextManifest).length === 0) {
|
|
164
|
-
declarations.length = 0;
|
|
165
|
-
}
|
|
147
|
+
const selectorModulesRemoved = await removeStaleSelectorModules(previousSelectorManifest, nextSelectorManifest);
|
|
148
|
+
await writeManifest(selectorModulesManifestPath, nextSelectorManifest);
|
|
166
149
|
return {
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
declarations,
|
|
150
|
+
selectorModulesWritten: selectorModuleWrites,
|
|
151
|
+
selectorModulesRemoved,
|
|
170
152
|
warnings,
|
|
171
|
-
|
|
172
|
-
typesIndexPath,
|
|
153
|
+
manifestPath: selectorModulesManifestPath,
|
|
173
154
|
};
|
|
174
155
|
}
|
|
175
156
|
function normalizeIncludeOptions(include, rootDir) {
|
|
@@ -227,18 +208,18 @@ async function findSpecifierImports(filePath) {
|
|
|
227
208
|
catch {
|
|
228
209
|
return [];
|
|
229
210
|
}
|
|
230
|
-
if (!source.includes(
|
|
211
|
+
if (!source.includes(SELECTOR_REFERENCE)) {
|
|
231
212
|
return [];
|
|
232
213
|
}
|
|
233
214
|
const matches = [];
|
|
234
215
|
const [imports] = (0, es_module_lexer_1.parse)(source, filePath);
|
|
235
216
|
for (const record of imports) {
|
|
236
217
|
const specifier = record.n ?? source.slice(record.s, record.e);
|
|
237
|
-
if (specifier && specifier.includes(
|
|
218
|
+
if (specifier && specifier.includes(SELECTOR_REFERENCE)) {
|
|
238
219
|
matches.push({ specifier, importer: filePath });
|
|
239
220
|
}
|
|
240
221
|
}
|
|
241
|
-
const requireRegex = /require\((['"])([^'"`]
|
|
222
|
+
const requireRegex = /require\((['"])([^'"`]+?\.knighted-css[^'"`]*)\1\)/g;
|
|
242
223
|
let reqMatch;
|
|
243
224
|
while ((reqMatch = requireRegex.exec(source)) !== null) {
|
|
244
225
|
const spec = reqMatch[2];
|
|
@@ -261,6 +242,21 @@ function splitResourceAndQuery(specifier) {
|
|
|
261
242
|
}
|
|
262
243
|
return { resource: trimmed.slice(0, queryIndex), query: trimmed.slice(queryIndex) };
|
|
263
244
|
}
|
|
245
|
+
function extractSelectorSourceSpecifier(specifier) {
|
|
246
|
+
const markerIndex = specifier.indexOf(SELECTOR_REFERENCE);
|
|
247
|
+
if (markerIndex < 0) {
|
|
248
|
+
return undefined;
|
|
249
|
+
}
|
|
250
|
+
const suffix = specifier.slice(markerIndex + SELECTOR_REFERENCE.length);
|
|
251
|
+
if (suffix.length > 0 && !/\.(?:[cm]?[tj]s|[tj]sx)$/.test(suffix)) {
|
|
252
|
+
return undefined;
|
|
253
|
+
}
|
|
254
|
+
const base = specifier.slice(0, markerIndex);
|
|
255
|
+
if (!base) {
|
|
256
|
+
return undefined;
|
|
257
|
+
}
|
|
258
|
+
return base;
|
|
259
|
+
}
|
|
264
260
|
const projectRequireCache = new Map();
|
|
265
261
|
async function resolveImportPath(resourceSpecifier, importerPath, rootDir, tsconfig) {
|
|
266
262
|
if (!resourceSpecifier)
|
|
@@ -283,47 +279,29 @@ async function resolveImportPath(resourceSpecifier, importerPath, rootDir, tscon
|
|
|
283
279
|
return undefined;
|
|
284
280
|
}
|
|
285
281
|
}
|
|
286
|
-
function
|
|
287
|
-
|
|
288
|
-
return `knt-${digest}.d.ts`;
|
|
289
|
-
}
|
|
290
|
-
function formatModuleDeclaration(specifier, variant, selectors) {
|
|
291
|
-
const literalSpecifier = JSON.stringify(specifier);
|
|
292
|
-
const selectorType = formatSelectorType(selectors);
|
|
293
|
-
const header = `declare module ${literalSpecifier} {`;
|
|
294
|
-
const footer = '}';
|
|
295
|
-
if (variant === 'types') {
|
|
296
|
-
return `${header}
|
|
297
|
-
export const knightedCss: string
|
|
298
|
-
export const stableSelectors: ${selectorType}
|
|
299
|
-
${footer}
|
|
300
|
-
`;
|
|
301
|
-
}
|
|
302
|
-
const stableLine = ` export const stableSelectors: ${selectorType}`;
|
|
303
|
-
const shared = ` const combined: KnightedCssCombinedModule<Record<string, unknown>>
|
|
304
|
-
export const knightedCss: string
|
|
305
|
-
${stableLine}`;
|
|
306
|
-
if (variant === 'combined') {
|
|
307
|
-
return `${header}
|
|
308
|
-
${shared}
|
|
309
|
-
export default combined
|
|
310
|
-
${footer}
|
|
311
|
-
`;
|
|
312
|
-
}
|
|
313
|
-
return `${header}
|
|
314
|
-
${shared}
|
|
315
|
-
${footer}
|
|
316
|
-
`;
|
|
282
|
+
function buildSelectorModuleManifestKey(resolvedPath) {
|
|
283
|
+
return resolvedPath.split(node_path_1.default.sep).join('/');
|
|
317
284
|
}
|
|
318
|
-
function
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
285
|
+
function buildSelectorModulePath(resolvedPath) {
|
|
286
|
+
return `${resolvedPath}${SELECTOR_MODULE_SUFFIX}`;
|
|
287
|
+
}
|
|
288
|
+
function formatSelectorModuleSource(selectors) {
|
|
289
|
+
const header = '// Generated by @knighted/css/generate-types\n// Do not edit.\n';
|
|
322
290
|
const entries = Array.from(selectors.entries()).sort(([a], [b]) => a.localeCompare(b));
|
|
323
|
-
const lines = entries.map(([token, selector]) => `
|
|
324
|
-
|
|
291
|
+
const lines = entries.map(([token, selector]) => ` ${JSON.stringify(token)}: ${JSON.stringify(selector)},`);
|
|
292
|
+
const literal = lines.length > 0
|
|
293
|
+
? `{
|
|
325
294
|
${lines.join('\n')}
|
|
326
|
-
|
|
295
|
+
} as const`
|
|
296
|
+
: '{} as const';
|
|
297
|
+
return `${header}
|
|
298
|
+
export const stableSelectors = ${literal}
|
|
299
|
+
|
|
300
|
+
export type KnightedCssStableSelectors = typeof stableSelectors
|
|
301
|
+
export type KnightedCssStableSelectorToken = keyof typeof stableSelectors
|
|
302
|
+
|
|
303
|
+
export default stableSelectors
|
|
304
|
+
`;
|
|
327
305
|
}
|
|
328
306
|
function hashContent(content) {
|
|
329
307
|
return node_crypto_1.default.createHash('sha1').update(content).digest('hex');
|
|
@@ -340,13 +318,12 @@ async function readManifest(manifestPath) {
|
|
|
340
318
|
async function writeManifest(manifestPath, manifest) {
|
|
341
319
|
await promises_1.default.writeFile(manifestPath, JSON.stringify(manifest, null, 2), 'utf8');
|
|
342
320
|
}
|
|
343
|
-
async function
|
|
344
|
-
const stale = Object.entries(previous).filter(([
|
|
321
|
+
async function removeStaleSelectorModules(previous, next) {
|
|
322
|
+
const stale = Object.entries(previous).filter(([key]) => !next[key]);
|
|
345
323
|
let removed = 0;
|
|
346
324
|
for (const [, entry] of stale) {
|
|
347
|
-
const targetPath = node_path_1.default.join(outDir, entry.file);
|
|
348
325
|
try {
|
|
349
|
-
await promises_1.default.unlink(
|
|
326
|
+
await promises_1.default.unlink(entry.file);
|
|
350
327
|
removed += 1;
|
|
351
328
|
}
|
|
352
329
|
catch {
|
|
@@ -355,25 +332,6 @@ async function removeStaleDeclarations(previous, next, outDir) {
|
|
|
355
332
|
}
|
|
356
333
|
return removed;
|
|
357
334
|
}
|
|
358
|
-
async function writeTypesIndex(indexPath, manifest, outDir) {
|
|
359
|
-
const header = '// Generated by @knighted/css/generate-types\n// Do not edit.\n';
|
|
360
|
-
const references = Object.values(manifest)
|
|
361
|
-
.sort((a, b) => a.file.localeCompare(b.file))
|
|
362
|
-
.map(entry => {
|
|
363
|
-
const rel = node_path_1.default
|
|
364
|
-
.relative(node_path_1.default.dirname(indexPath), node_path_1.default.join(outDir, entry.file))
|
|
365
|
-
.split(node_path_1.default.sep)
|
|
366
|
-
.join('/');
|
|
367
|
-
return `/// <reference path="${rel}" />`;
|
|
368
|
-
});
|
|
369
|
-
const content = references.length > 0
|
|
370
|
-
? `${header}
|
|
371
|
-
${references.join('\n')}
|
|
372
|
-
`
|
|
373
|
-
: `${header}
|
|
374
|
-
`;
|
|
375
|
-
await promises_1.default.writeFile(indexPath, content, 'utf8');
|
|
376
|
-
}
|
|
377
335
|
function formatErrorMessage(error) {
|
|
378
336
|
if (error instanceof Error && typeof error.message === 'string') {
|
|
379
337
|
return error.message;
|
|
@@ -383,6 +341,23 @@ function formatErrorMessage(error) {
|
|
|
383
341
|
function relativeToRoot(filePath, rootDir) {
|
|
384
342
|
return node_path_1.default.relative(rootDir, filePath) || filePath;
|
|
385
343
|
}
|
|
344
|
+
function isWithinRoot(filePath, rootDir) {
|
|
345
|
+
const relative = node_path_1.default.relative(rootDir, filePath);
|
|
346
|
+
return relative === '' || (!relative.startsWith('..') && !node_path_1.default.isAbsolute(relative));
|
|
347
|
+
}
|
|
348
|
+
async function ensureSelectorModule(resolvedPath, selectors, previousManifest, nextManifest) {
|
|
349
|
+
const manifestKey = buildSelectorModuleManifestKey(resolvedPath);
|
|
350
|
+
const targetPath = buildSelectorModulePath(resolvedPath);
|
|
351
|
+
const source = formatSelectorModuleSource(selectors);
|
|
352
|
+
const hash = hashContent(source);
|
|
353
|
+
const previousEntry = previousManifest[manifestKey];
|
|
354
|
+
const needsWrite = previousEntry?.hash !== hash || !(await fileExists(targetPath));
|
|
355
|
+
if (needsWrite) {
|
|
356
|
+
await promises_1.default.writeFile(targetPath, source, 'utf8');
|
|
357
|
+
}
|
|
358
|
+
nextManifest[manifestKey] = { file: targetPath, hash };
|
|
359
|
+
return needsWrite;
|
|
360
|
+
}
|
|
386
361
|
async function fileExists(target) {
|
|
387
362
|
try {
|
|
388
363
|
await promises_1.default.access(target);
|
|
@@ -506,7 +481,6 @@ async function runGenerateTypesCli(argv = process.argv.slice(2)) {
|
|
|
506
481
|
rootDir: parsed.rootDir,
|
|
507
482
|
include: parsed.include,
|
|
508
483
|
outDir: parsed.outDir,
|
|
509
|
-
typesRoot: parsed.typesRoot,
|
|
510
484
|
stableNamespace: parsed.stableNamespace,
|
|
511
485
|
});
|
|
512
486
|
reportCliResult(result);
|
|
@@ -521,12 +495,11 @@ function parseCliArgs(argv) {
|
|
|
521
495
|
let rootDir = process.cwd();
|
|
522
496
|
const include = [];
|
|
523
497
|
let outDir;
|
|
524
|
-
let typesRoot;
|
|
525
498
|
let stableNamespace;
|
|
526
499
|
for (let i = 0; i < argv.length; i += 1) {
|
|
527
500
|
const arg = argv[i];
|
|
528
501
|
if (arg === '--help' || arg === '-h') {
|
|
529
|
-
return { rootDir, include, outDir,
|
|
502
|
+
return { rootDir, include, outDir, stableNamespace, help: true };
|
|
530
503
|
}
|
|
531
504
|
if (arg === '--root' || arg === '-r') {
|
|
532
505
|
const value = argv[++i];
|
|
@@ -552,14 +525,6 @@ function parseCliArgs(argv) {
|
|
|
552
525
|
outDir = value;
|
|
553
526
|
continue;
|
|
554
527
|
}
|
|
555
|
-
if (arg === '--types-root') {
|
|
556
|
-
const value = argv[++i];
|
|
557
|
-
if (!value) {
|
|
558
|
-
throw new Error('Missing value for --types-root');
|
|
559
|
-
}
|
|
560
|
-
typesRoot = value;
|
|
561
|
-
continue;
|
|
562
|
-
}
|
|
563
528
|
if (arg === '--stable-namespace') {
|
|
564
529
|
const value = argv[++i];
|
|
565
530
|
if (!value) {
|
|
@@ -573,7 +538,7 @@ function parseCliArgs(argv) {
|
|
|
573
538
|
}
|
|
574
539
|
include.push(arg);
|
|
575
540
|
}
|
|
576
|
-
return { rootDir, include, outDir,
|
|
541
|
+
return { rootDir, include, outDir, stableNamespace };
|
|
577
542
|
}
|
|
578
543
|
function printHelp() {
|
|
579
544
|
console.log(`Usage: knighted-css-generate-types [options]
|
|
@@ -581,20 +546,19 @@ function printHelp() {
|
|
|
581
546
|
Options:
|
|
582
547
|
-r, --root <path> Project root directory (default: cwd)
|
|
583
548
|
-i, --include <path> Additional directories/files to scan (repeatable)
|
|
584
|
-
--out-dir <path>
|
|
585
|
-
--types-root <path> Directory for generated @types entrypoint
|
|
549
|
+
--out-dir <path> Directory to store selector module manifest cache
|
|
586
550
|
--stable-namespace <name> Stable namespace prefix for generated selector maps
|
|
587
551
|
-h, --help Show this help message
|
|
588
552
|
`);
|
|
589
553
|
}
|
|
590
554
|
function reportCliResult(result) {
|
|
591
|
-
if (result.
|
|
592
|
-
console.log('[knighted-css]
|
|
555
|
+
if (result.selectorModulesWritten === 0 && result.selectorModulesRemoved === 0) {
|
|
556
|
+
console.log('[knighted-css] Selector modules are up to date.');
|
|
593
557
|
}
|
|
594
558
|
else {
|
|
595
|
-
console.log(`[knighted-css]
|
|
559
|
+
console.log(`[knighted-css] Selector modules updated: wrote ${result.selectorModulesWritten}, removed ${result.selectorModulesRemoved}.`);
|
|
596
560
|
}
|
|
597
|
-
console.log(`[knighted-css]
|
|
561
|
+
console.log(`[knighted-css] Manifest: ${result.manifestPath}`);
|
|
598
562
|
for (const warning of result.warnings) {
|
|
599
563
|
console.warn(`[knighted-css] ${warning}`);
|
|
600
564
|
}
|
|
@@ -609,15 +573,12 @@ function setImportMetaUrlProvider(provider) {
|
|
|
609
573
|
importMetaUrlProvider = provider ?? getImportMetaUrl;
|
|
610
574
|
}
|
|
611
575
|
exports.__generateTypesInternals = {
|
|
612
|
-
writeTypesIndex,
|
|
613
576
|
stripInlineLoader,
|
|
614
577
|
splitResourceAndQuery,
|
|
578
|
+
extractSelectorSourceSpecifier,
|
|
615
579
|
findSpecifierImports,
|
|
616
580
|
resolveImportPath,
|
|
617
581
|
resolvePackageRoot,
|
|
618
|
-
buildDeclarationFileName,
|
|
619
|
-
formatModuleDeclaration,
|
|
620
|
-
formatSelectorType,
|
|
621
582
|
relativeToRoot,
|
|
622
583
|
collectCandidateFiles,
|
|
623
584
|
normalizeIncludeOptions,
|
|
@@ -633,4 +594,11 @@ exports.__generateTypesInternals = {
|
|
|
633
594
|
parseCliArgs,
|
|
634
595
|
printHelp,
|
|
635
596
|
reportCliResult,
|
|
597
|
+
buildSelectorModuleManifestKey,
|
|
598
|
+
buildSelectorModulePath,
|
|
599
|
+
formatSelectorModuleSource,
|
|
600
|
+
ensureSelectorModule,
|
|
601
|
+
removeStaleSelectorModules,
|
|
602
|
+
readManifest,
|
|
603
|
+
writeManifest,
|
|
636
604
|
};
|
|
@@ -3,38 +3,30 @@ import { moduleType } from 'node-module-type';
|
|
|
3
3
|
import { getTsconfig } from 'get-tsconfig';
|
|
4
4
|
import { type MatchPath } from 'tsconfig-paths';
|
|
5
5
|
import { cssWithMeta } from './css.cjs';
|
|
6
|
-
import { type SelectorTypeVariant } from './loaderInternals.cjs';
|
|
7
|
-
interface ManifestEntry {
|
|
8
|
-
file: string;
|
|
9
|
-
hash: string;
|
|
10
|
-
}
|
|
11
|
-
type Manifest = Record<string, ManifestEntry>;
|
|
12
6
|
interface ImportMatch {
|
|
13
7
|
specifier: string;
|
|
14
8
|
importer: string;
|
|
15
9
|
}
|
|
16
|
-
interface
|
|
17
|
-
|
|
18
|
-
|
|
10
|
+
interface ManifestEntry {
|
|
11
|
+
file: string;
|
|
12
|
+
hash: string;
|
|
19
13
|
}
|
|
14
|
+
type SelectorModuleManifest = Record<string, ManifestEntry>;
|
|
20
15
|
interface TsconfigResolutionContext {
|
|
21
16
|
absoluteBaseUrl?: string;
|
|
22
17
|
matchPath?: MatchPath;
|
|
23
18
|
}
|
|
24
19
|
type CssWithMetaFn = typeof cssWithMeta;
|
|
25
20
|
export interface GenerateTypesResult {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
declarations: DeclarationRecord[];
|
|
21
|
+
selectorModulesWritten: number;
|
|
22
|
+
selectorModulesRemoved: number;
|
|
29
23
|
warnings: string[];
|
|
30
|
-
|
|
31
|
-
typesIndexPath: string;
|
|
24
|
+
manifestPath: string;
|
|
32
25
|
}
|
|
33
26
|
export interface GenerateTypesOptions {
|
|
34
27
|
rootDir?: string;
|
|
35
28
|
include?: string[];
|
|
36
29
|
outDir?: string;
|
|
37
|
-
typesRoot?: string;
|
|
38
30
|
stableNamespace?: string;
|
|
39
31
|
}
|
|
40
32
|
type ModuleTypeDetector = () => ReturnType<typeof moduleType>;
|
|
@@ -48,12 +40,16 @@ declare function splitResourceAndQuery(specifier: string): {
|
|
|
48
40
|
resource: string;
|
|
49
41
|
query: string;
|
|
50
42
|
};
|
|
43
|
+
declare function extractSelectorSourceSpecifier(specifier: string): string | undefined;
|
|
51
44
|
declare function resolveImportPath(resourceSpecifier: string, importerPath: string, rootDir: string, tsconfig?: TsconfigResolutionContext): Promise<string | undefined>;
|
|
52
|
-
declare function
|
|
53
|
-
declare function
|
|
54
|
-
declare function
|
|
55
|
-
declare function
|
|
45
|
+
declare function buildSelectorModuleManifestKey(resolvedPath: string): string;
|
|
46
|
+
declare function buildSelectorModulePath(resolvedPath: string): string;
|
|
47
|
+
declare function formatSelectorModuleSource(selectors: Map<string, string>): string;
|
|
48
|
+
declare function readManifest(manifestPath: string): Promise<SelectorModuleManifest>;
|
|
49
|
+
declare function writeManifest(manifestPath: string, manifest: SelectorModuleManifest): Promise<void>;
|
|
50
|
+
declare function removeStaleSelectorModules(previous: SelectorModuleManifest, next: SelectorModuleManifest): Promise<number>;
|
|
56
51
|
declare function relativeToRoot(filePath: string, rootDir: string): string;
|
|
52
|
+
declare function ensureSelectorModule(resolvedPath: string, selectors: Map<string, string>, previousManifest: SelectorModuleManifest, nextManifest: SelectorModuleManifest): Promise<boolean>;
|
|
57
53
|
declare function resolveWithTsconfigPaths(specifier: string, tsconfig?: TsconfigResolutionContext): Promise<string | undefined>;
|
|
58
54
|
declare function loadTsconfigResolutionContext(rootDir: string, loader?: typeof getTsconfig): TsconfigResolutionContext | undefined;
|
|
59
55
|
declare function normalizeTsconfigPaths(paths: Record<string, string[] | string> | undefined): Record<string, string[]> | undefined;
|
|
@@ -65,7 +61,6 @@ export interface ParsedCliArgs {
|
|
|
65
61
|
rootDir: string;
|
|
66
62
|
include?: string[];
|
|
67
63
|
outDir?: string;
|
|
68
|
-
typesRoot?: string;
|
|
69
64
|
stableNamespace?: string;
|
|
70
65
|
help?: boolean;
|
|
71
66
|
}
|
|
@@ -76,15 +71,12 @@ declare function setCssWithMetaImplementation(impl?: CssWithMetaFn): void;
|
|
|
76
71
|
declare function setModuleTypeDetector(detector?: ModuleTypeDetector): void;
|
|
77
72
|
declare function setImportMetaUrlProvider(provider?: () => string | undefined): void;
|
|
78
73
|
export declare const __generateTypesInternals: {
|
|
79
|
-
writeTypesIndex: typeof writeTypesIndex;
|
|
80
74
|
stripInlineLoader: typeof stripInlineLoader;
|
|
81
75
|
splitResourceAndQuery: typeof splitResourceAndQuery;
|
|
76
|
+
extractSelectorSourceSpecifier: typeof extractSelectorSourceSpecifier;
|
|
82
77
|
findSpecifierImports: typeof findSpecifierImports;
|
|
83
78
|
resolveImportPath: typeof resolveImportPath;
|
|
84
79
|
resolvePackageRoot: typeof resolvePackageRoot;
|
|
85
|
-
buildDeclarationFileName: typeof buildDeclarationFileName;
|
|
86
|
-
formatModuleDeclaration: typeof formatModuleDeclaration;
|
|
87
|
-
formatSelectorType: typeof formatSelectorType;
|
|
88
80
|
relativeToRoot: typeof relativeToRoot;
|
|
89
81
|
collectCandidateFiles: typeof collectCandidateFiles;
|
|
90
82
|
normalizeIncludeOptions: typeof normalizeIncludeOptions;
|
|
@@ -100,5 +92,12 @@ export declare const __generateTypesInternals: {
|
|
|
100
92
|
parseCliArgs: typeof parseCliArgs;
|
|
101
93
|
printHelp: typeof printHelp;
|
|
102
94
|
reportCliResult: typeof reportCliResult;
|
|
95
|
+
buildSelectorModuleManifestKey: typeof buildSelectorModuleManifestKey;
|
|
96
|
+
buildSelectorModulePath: typeof buildSelectorModulePath;
|
|
97
|
+
formatSelectorModuleSource: typeof formatSelectorModuleSource;
|
|
98
|
+
ensureSelectorModule: typeof ensureSelectorModule;
|
|
99
|
+
removeStaleSelectorModules: typeof removeStaleSelectorModules;
|
|
100
|
+
readManifest: typeof readManifest;
|
|
101
|
+
writeManifest: typeof writeManifest;
|
|
103
102
|
};
|
|
104
103
|
export {};
|
package/dist/generateTypes.d.ts
CHANGED
|
@@ -3,38 +3,30 @@ import { moduleType } from 'node-module-type';
|
|
|
3
3
|
import { getTsconfig } from 'get-tsconfig';
|
|
4
4
|
import { type MatchPath } from 'tsconfig-paths';
|
|
5
5
|
import { cssWithMeta } from './css.js';
|
|
6
|
-
import { type SelectorTypeVariant } from './loaderInternals.js';
|
|
7
|
-
interface ManifestEntry {
|
|
8
|
-
file: string;
|
|
9
|
-
hash: string;
|
|
10
|
-
}
|
|
11
|
-
type Manifest = Record<string, ManifestEntry>;
|
|
12
6
|
interface ImportMatch {
|
|
13
7
|
specifier: string;
|
|
14
8
|
importer: string;
|
|
15
9
|
}
|
|
16
|
-
interface
|
|
17
|
-
|
|
18
|
-
|
|
10
|
+
interface ManifestEntry {
|
|
11
|
+
file: string;
|
|
12
|
+
hash: string;
|
|
19
13
|
}
|
|
14
|
+
type SelectorModuleManifest = Record<string, ManifestEntry>;
|
|
20
15
|
interface TsconfigResolutionContext {
|
|
21
16
|
absoluteBaseUrl?: string;
|
|
22
17
|
matchPath?: MatchPath;
|
|
23
18
|
}
|
|
24
19
|
type CssWithMetaFn = typeof cssWithMeta;
|
|
25
20
|
export interface GenerateTypesResult {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
declarations: DeclarationRecord[];
|
|
21
|
+
selectorModulesWritten: number;
|
|
22
|
+
selectorModulesRemoved: number;
|
|
29
23
|
warnings: string[];
|
|
30
|
-
|
|
31
|
-
typesIndexPath: string;
|
|
24
|
+
manifestPath: string;
|
|
32
25
|
}
|
|
33
26
|
export interface GenerateTypesOptions {
|
|
34
27
|
rootDir?: string;
|
|
35
28
|
include?: string[];
|
|
36
29
|
outDir?: string;
|
|
37
|
-
typesRoot?: string;
|
|
38
30
|
stableNamespace?: string;
|
|
39
31
|
}
|
|
40
32
|
type ModuleTypeDetector = () => ReturnType<typeof moduleType>;
|
|
@@ -48,12 +40,16 @@ declare function splitResourceAndQuery(specifier: string): {
|
|
|
48
40
|
resource: string;
|
|
49
41
|
query: string;
|
|
50
42
|
};
|
|
43
|
+
declare function extractSelectorSourceSpecifier(specifier: string): string | undefined;
|
|
51
44
|
declare function resolveImportPath(resourceSpecifier: string, importerPath: string, rootDir: string, tsconfig?: TsconfigResolutionContext): Promise<string | undefined>;
|
|
52
|
-
declare function
|
|
53
|
-
declare function
|
|
54
|
-
declare function
|
|
55
|
-
declare function
|
|
45
|
+
declare function buildSelectorModuleManifestKey(resolvedPath: string): string;
|
|
46
|
+
declare function buildSelectorModulePath(resolvedPath: string): string;
|
|
47
|
+
declare function formatSelectorModuleSource(selectors: Map<string, string>): string;
|
|
48
|
+
declare function readManifest(manifestPath: string): Promise<SelectorModuleManifest>;
|
|
49
|
+
declare function writeManifest(manifestPath: string, manifest: SelectorModuleManifest): Promise<void>;
|
|
50
|
+
declare function removeStaleSelectorModules(previous: SelectorModuleManifest, next: SelectorModuleManifest): Promise<number>;
|
|
56
51
|
declare function relativeToRoot(filePath: string, rootDir: string): string;
|
|
52
|
+
declare function ensureSelectorModule(resolvedPath: string, selectors: Map<string, string>, previousManifest: SelectorModuleManifest, nextManifest: SelectorModuleManifest): Promise<boolean>;
|
|
57
53
|
declare function resolveWithTsconfigPaths(specifier: string, tsconfig?: TsconfigResolutionContext): Promise<string | undefined>;
|
|
58
54
|
declare function loadTsconfigResolutionContext(rootDir: string, loader?: typeof getTsconfig): TsconfigResolutionContext | undefined;
|
|
59
55
|
declare function normalizeTsconfigPaths(paths: Record<string, string[] | string> | undefined): Record<string, string[]> | undefined;
|
|
@@ -65,7 +61,6 @@ export interface ParsedCliArgs {
|
|
|
65
61
|
rootDir: string;
|
|
66
62
|
include?: string[];
|
|
67
63
|
outDir?: string;
|
|
68
|
-
typesRoot?: string;
|
|
69
64
|
stableNamespace?: string;
|
|
70
65
|
help?: boolean;
|
|
71
66
|
}
|
|
@@ -76,15 +71,12 @@ declare function setCssWithMetaImplementation(impl?: CssWithMetaFn): void;
|
|
|
76
71
|
declare function setModuleTypeDetector(detector?: ModuleTypeDetector): void;
|
|
77
72
|
declare function setImportMetaUrlProvider(provider?: () => string | undefined): void;
|
|
78
73
|
export declare const __generateTypesInternals: {
|
|
79
|
-
writeTypesIndex: typeof writeTypesIndex;
|
|
80
74
|
stripInlineLoader: typeof stripInlineLoader;
|
|
81
75
|
splitResourceAndQuery: typeof splitResourceAndQuery;
|
|
76
|
+
extractSelectorSourceSpecifier: typeof extractSelectorSourceSpecifier;
|
|
82
77
|
findSpecifierImports: typeof findSpecifierImports;
|
|
83
78
|
resolveImportPath: typeof resolveImportPath;
|
|
84
79
|
resolvePackageRoot: typeof resolvePackageRoot;
|
|
85
|
-
buildDeclarationFileName: typeof buildDeclarationFileName;
|
|
86
|
-
formatModuleDeclaration: typeof formatModuleDeclaration;
|
|
87
|
-
formatSelectorType: typeof formatSelectorType;
|
|
88
80
|
relativeToRoot: typeof relativeToRoot;
|
|
89
81
|
collectCandidateFiles: typeof collectCandidateFiles;
|
|
90
82
|
normalizeIncludeOptions: typeof normalizeIncludeOptions;
|
|
@@ -100,5 +92,12 @@ export declare const __generateTypesInternals: {
|
|
|
100
92
|
parseCliArgs: typeof parseCliArgs;
|
|
101
93
|
printHelp: typeof printHelp;
|
|
102
94
|
reportCliResult: typeof reportCliResult;
|
|
95
|
+
buildSelectorModuleManifestKey: typeof buildSelectorModuleManifestKey;
|
|
96
|
+
buildSelectorModulePath: typeof buildSelectorModulePath;
|
|
97
|
+
formatSelectorModuleSource: typeof formatSelectorModuleSource;
|
|
98
|
+
ensureSelectorModule: typeof ensureSelectorModule;
|
|
99
|
+
removeStaleSelectorModules: typeof removeStaleSelectorModules;
|
|
100
|
+
readManifest: typeof readManifest;
|
|
101
|
+
writeManifest: typeof writeManifest;
|
|
103
102
|
};
|
|
104
103
|
export {};
|
package/dist/generateTypes.js
CHANGED
|
@@ -8,7 +8,6 @@ import { moduleType } from 'node-module-type';
|
|
|
8
8
|
import { getTsconfig } from 'get-tsconfig';
|
|
9
9
|
import { createMatchPath } from 'tsconfig-paths';
|
|
10
10
|
import { cssWithMeta } from './css.js';
|
|
11
|
-
import { determineSelectorVariant, hasQueryFlag, TYPES_QUERY_FLAG, } from './loaderInternals.js';
|
|
12
11
|
import { buildStableSelectorsLiteral } from './stableSelectorsLiteral.js';
|
|
13
12
|
import { resolveStableNamespace } from './stableNamespace.js';
|
|
14
13
|
let activeCssWithMeta = cssWithMeta;
|
|
@@ -57,22 +56,19 @@ function getImportMetaUrl() {
|
|
|
57
56
|
}
|
|
58
57
|
}
|
|
59
58
|
const PACKAGE_ROOT = resolvePackageRoot();
|
|
60
|
-
const
|
|
61
|
-
const
|
|
59
|
+
const SELECTOR_REFERENCE = '.knighted-css';
|
|
60
|
+
const SELECTOR_MODULE_SUFFIX = '.knighted-css.ts';
|
|
62
61
|
export async function generateTypes(options = {}) {
|
|
63
62
|
const rootDir = path.resolve(options.rootDir ?? process.cwd());
|
|
64
63
|
const include = normalizeIncludeOptions(options.include, rootDir);
|
|
65
|
-
const
|
|
66
|
-
const typesRoot = path.resolve(options.typesRoot ?? DEFAULT_TYPES_ROOT);
|
|
64
|
+
const cacheDir = path.resolve(options.outDir ?? path.join(rootDir, '.knighted-css'));
|
|
67
65
|
const tsconfig = loadTsconfigResolutionContext(rootDir);
|
|
68
66
|
await init;
|
|
69
|
-
await fs.mkdir(
|
|
70
|
-
await fs.mkdir(typesRoot, { recursive: true });
|
|
67
|
+
await fs.mkdir(cacheDir, { recursive: true });
|
|
71
68
|
const internalOptions = {
|
|
72
69
|
rootDir,
|
|
73
70
|
include,
|
|
74
|
-
|
|
75
|
-
typesRoot,
|
|
71
|
+
cacheDir,
|
|
76
72
|
stableNamespace: options.stableNamespace,
|
|
77
73
|
tsconfig,
|
|
78
74
|
};
|
|
@@ -81,32 +77,27 @@ export async function generateTypes(options = {}) {
|
|
|
81
77
|
async function generateDeclarations(options) {
|
|
82
78
|
const peerResolver = createProjectPeerResolver(options.rootDir);
|
|
83
79
|
const files = await collectCandidateFiles(options.include);
|
|
84
|
-
const
|
|
85
|
-
const
|
|
86
|
-
const
|
|
80
|
+
const selectorModulesManifestPath = path.join(options.cacheDir, 'selector-modules.json');
|
|
81
|
+
const previousSelectorManifest = await readManifest(selectorModulesManifestPath);
|
|
82
|
+
const nextSelectorManifest = {};
|
|
87
83
|
const selectorCache = new Map();
|
|
88
|
-
const
|
|
89
|
-
const declarations = [];
|
|
84
|
+
const processedSelectors = new Set();
|
|
90
85
|
const warnings = [];
|
|
91
|
-
let
|
|
86
|
+
let selectorModuleWrites = 0;
|
|
92
87
|
for (const filePath of files) {
|
|
93
88
|
const matches = await findSpecifierImports(filePath);
|
|
94
89
|
for (const match of matches) {
|
|
95
90
|
const cleaned = match.specifier.trim();
|
|
96
91
|
const inlineFree = stripInlineLoader(cleaned);
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
if (!query || !hasQueryFlag(query, TYPES_QUERY_FLAG)) {
|
|
101
|
-
continue;
|
|
102
|
-
}
|
|
103
|
-
if (processedSpecifiers.has(cleaned)) {
|
|
92
|
+
const { resource } = splitResourceAndQuery(inlineFree);
|
|
93
|
+
const selectorSource = extractSelectorSourceSpecifier(resource);
|
|
94
|
+
if (!selectorSource) {
|
|
104
95
|
continue;
|
|
105
96
|
}
|
|
106
97
|
const resolvedNamespace = resolveStableNamespace(options.stableNamespace);
|
|
107
|
-
const resolvedPath = await resolveImportPath(
|
|
98
|
+
const resolvedPath = await resolveImportPath(selectorSource, match.importer, options.rootDir, options.tsconfig);
|
|
108
99
|
if (!resolvedPath) {
|
|
109
|
-
warnings.push(`Unable to resolve ${
|
|
100
|
+
warnings.push(`Unable to resolve ${selectorSource} referenced by ${relativeToRoot(match.importer, options.rootDir)}.`);
|
|
110
101
|
continue;
|
|
111
102
|
}
|
|
112
103
|
const cacheKey = `${resolvedPath}::${resolvedNamespace}`;
|
|
@@ -130,38 +121,28 @@ async function generateDeclarations(options) {
|
|
|
130
121
|
}
|
|
131
122
|
selectorCache.set(cacheKey, selectorMap);
|
|
132
123
|
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
const
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
if (needsWrite) {
|
|
141
|
-
await fs.writeFile(targetPath, declaration, 'utf8');
|
|
142
|
-
writes += 1;
|
|
124
|
+
if (!isWithinRoot(resolvedPath, options.rootDir)) {
|
|
125
|
+
warnings.push(`Skipping selector module for ${relativeToRoot(resolvedPath, options.rootDir)} because it is outside the project root.`);
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
const manifestKey = buildSelectorModuleManifestKey(resolvedPath);
|
|
129
|
+
if (processedSelectors.has(manifestKey)) {
|
|
130
|
+
continue;
|
|
143
131
|
}
|
|
144
|
-
|
|
145
|
-
if (
|
|
146
|
-
|
|
132
|
+
const moduleWrite = await ensureSelectorModule(resolvedPath, selectorMap, previousSelectorManifest, nextSelectorManifest);
|
|
133
|
+
if (moduleWrite) {
|
|
134
|
+
selectorModuleWrites += 1;
|
|
147
135
|
}
|
|
148
|
-
|
|
136
|
+
processedSelectors.add(manifestKey);
|
|
149
137
|
}
|
|
150
138
|
}
|
|
151
|
-
const
|
|
152
|
-
await writeManifest(
|
|
153
|
-
const typesIndexPath = path.join(options.typesRoot, 'index.d.ts');
|
|
154
|
-
await writeTypesIndex(typesIndexPath, nextManifest, options.outDir);
|
|
155
|
-
if (Object.keys(nextManifest).length === 0) {
|
|
156
|
-
declarations.length = 0;
|
|
157
|
-
}
|
|
139
|
+
const selectorModulesRemoved = await removeStaleSelectorModules(previousSelectorManifest, nextSelectorManifest);
|
|
140
|
+
await writeManifest(selectorModulesManifestPath, nextSelectorManifest);
|
|
158
141
|
return {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
declarations,
|
|
142
|
+
selectorModulesWritten: selectorModuleWrites,
|
|
143
|
+
selectorModulesRemoved,
|
|
162
144
|
warnings,
|
|
163
|
-
|
|
164
|
-
typesIndexPath,
|
|
145
|
+
manifestPath: selectorModulesManifestPath,
|
|
165
146
|
};
|
|
166
147
|
}
|
|
167
148
|
function normalizeIncludeOptions(include, rootDir) {
|
|
@@ -219,18 +200,18 @@ async function findSpecifierImports(filePath) {
|
|
|
219
200
|
catch {
|
|
220
201
|
return [];
|
|
221
202
|
}
|
|
222
|
-
if (!source.includes(
|
|
203
|
+
if (!source.includes(SELECTOR_REFERENCE)) {
|
|
223
204
|
return [];
|
|
224
205
|
}
|
|
225
206
|
const matches = [];
|
|
226
207
|
const [imports] = parse(source, filePath);
|
|
227
208
|
for (const record of imports) {
|
|
228
209
|
const specifier = record.n ?? source.slice(record.s, record.e);
|
|
229
|
-
if (specifier && specifier.includes(
|
|
210
|
+
if (specifier && specifier.includes(SELECTOR_REFERENCE)) {
|
|
230
211
|
matches.push({ specifier, importer: filePath });
|
|
231
212
|
}
|
|
232
213
|
}
|
|
233
|
-
const requireRegex = /require\((['"])([^'"`]
|
|
214
|
+
const requireRegex = /require\((['"])([^'"`]+?\.knighted-css[^'"`]*)\1\)/g;
|
|
234
215
|
let reqMatch;
|
|
235
216
|
while ((reqMatch = requireRegex.exec(source)) !== null) {
|
|
236
217
|
const spec = reqMatch[2];
|
|
@@ -253,6 +234,21 @@ function splitResourceAndQuery(specifier) {
|
|
|
253
234
|
}
|
|
254
235
|
return { resource: trimmed.slice(0, queryIndex), query: trimmed.slice(queryIndex) };
|
|
255
236
|
}
|
|
237
|
+
function extractSelectorSourceSpecifier(specifier) {
|
|
238
|
+
const markerIndex = specifier.indexOf(SELECTOR_REFERENCE);
|
|
239
|
+
if (markerIndex < 0) {
|
|
240
|
+
return undefined;
|
|
241
|
+
}
|
|
242
|
+
const suffix = specifier.slice(markerIndex + SELECTOR_REFERENCE.length);
|
|
243
|
+
if (suffix.length > 0 && !/\.(?:[cm]?[tj]s|[tj]sx)$/.test(suffix)) {
|
|
244
|
+
return undefined;
|
|
245
|
+
}
|
|
246
|
+
const base = specifier.slice(0, markerIndex);
|
|
247
|
+
if (!base) {
|
|
248
|
+
return undefined;
|
|
249
|
+
}
|
|
250
|
+
return base;
|
|
251
|
+
}
|
|
256
252
|
const projectRequireCache = new Map();
|
|
257
253
|
async function resolveImportPath(resourceSpecifier, importerPath, rootDir, tsconfig) {
|
|
258
254
|
if (!resourceSpecifier)
|
|
@@ -275,47 +271,29 @@ async function resolveImportPath(resourceSpecifier, importerPath, rootDir, tscon
|
|
|
275
271
|
return undefined;
|
|
276
272
|
}
|
|
277
273
|
}
|
|
278
|
-
function
|
|
279
|
-
|
|
280
|
-
return `knt-${digest}.d.ts`;
|
|
281
|
-
}
|
|
282
|
-
function formatModuleDeclaration(specifier, variant, selectors) {
|
|
283
|
-
const literalSpecifier = JSON.stringify(specifier);
|
|
284
|
-
const selectorType = formatSelectorType(selectors);
|
|
285
|
-
const header = `declare module ${literalSpecifier} {`;
|
|
286
|
-
const footer = '}';
|
|
287
|
-
if (variant === 'types') {
|
|
288
|
-
return `${header}
|
|
289
|
-
export const knightedCss: string
|
|
290
|
-
export const stableSelectors: ${selectorType}
|
|
291
|
-
${footer}
|
|
292
|
-
`;
|
|
293
|
-
}
|
|
294
|
-
const stableLine = ` export const stableSelectors: ${selectorType}`;
|
|
295
|
-
const shared = ` const combined: KnightedCssCombinedModule<Record<string, unknown>>
|
|
296
|
-
export const knightedCss: string
|
|
297
|
-
${stableLine}`;
|
|
298
|
-
if (variant === 'combined') {
|
|
299
|
-
return `${header}
|
|
300
|
-
${shared}
|
|
301
|
-
export default combined
|
|
302
|
-
${footer}
|
|
303
|
-
`;
|
|
304
|
-
}
|
|
305
|
-
return `${header}
|
|
306
|
-
${shared}
|
|
307
|
-
${footer}
|
|
308
|
-
`;
|
|
274
|
+
function buildSelectorModuleManifestKey(resolvedPath) {
|
|
275
|
+
return resolvedPath.split(path.sep).join('/');
|
|
309
276
|
}
|
|
310
|
-
function
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
277
|
+
function buildSelectorModulePath(resolvedPath) {
|
|
278
|
+
return `${resolvedPath}${SELECTOR_MODULE_SUFFIX}`;
|
|
279
|
+
}
|
|
280
|
+
function formatSelectorModuleSource(selectors) {
|
|
281
|
+
const header = '// Generated by @knighted/css/generate-types\n// Do not edit.\n';
|
|
314
282
|
const entries = Array.from(selectors.entries()).sort(([a], [b]) => a.localeCompare(b));
|
|
315
|
-
const lines = entries.map(([token, selector]) => `
|
|
316
|
-
|
|
283
|
+
const lines = entries.map(([token, selector]) => ` ${JSON.stringify(token)}: ${JSON.stringify(selector)},`);
|
|
284
|
+
const literal = lines.length > 0
|
|
285
|
+
? `{
|
|
317
286
|
${lines.join('\n')}
|
|
318
|
-
|
|
287
|
+
} as const`
|
|
288
|
+
: '{} as const';
|
|
289
|
+
return `${header}
|
|
290
|
+
export const stableSelectors = ${literal}
|
|
291
|
+
|
|
292
|
+
export type KnightedCssStableSelectors = typeof stableSelectors
|
|
293
|
+
export type KnightedCssStableSelectorToken = keyof typeof stableSelectors
|
|
294
|
+
|
|
295
|
+
export default stableSelectors
|
|
296
|
+
`;
|
|
319
297
|
}
|
|
320
298
|
function hashContent(content) {
|
|
321
299
|
return crypto.createHash('sha1').update(content).digest('hex');
|
|
@@ -332,13 +310,12 @@ async function readManifest(manifestPath) {
|
|
|
332
310
|
async function writeManifest(manifestPath, manifest) {
|
|
333
311
|
await fs.writeFile(manifestPath, JSON.stringify(manifest, null, 2), 'utf8');
|
|
334
312
|
}
|
|
335
|
-
async function
|
|
336
|
-
const stale = Object.entries(previous).filter(([
|
|
313
|
+
async function removeStaleSelectorModules(previous, next) {
|
|
314
|
+
const stale = Object.entries(previous).filter(([key]) => !next[key]);
|
|
337
315
|
let removed = 0;
|
|
338
316
|
for (const [, entry] of stale) {
|
|
339
|
-
const targetPath = path.join(outDir, entry.file);
|
|
340
317
|
try {
|
|
341
|
-
await fs.unlink(
|
|
318
|
+
await fs.unlink(entry.file);
|
|
342
319
|
removed += 1;
|
|
343
320
|
}
|
|
344
321
|
catch {
|
|
@@ -347,25 +324,6 @@ async function removeStaleDeclarations(previous, next, outDir) {
|
|
|
347
324
|
}
|
|
348
325
|
return removed;
|
|
349
326
|
}
|
|
350
|
-
async function writeTypesIndex(indexPath, manifest, outDir) {
|
|
351
|
-
const header = '// Generated by @knighted/css/generate-types\n// Do not edit.\n';
|
|
352
|
-
const references = Object.values(manifest)
|
|
353
|
-
.sort((a, b) => a.file.localeCompare(b.file))
|
|
354
|
-
.map(entry => {
|
|
355
|
-
const rel = path
|
|
356
|
-
.relative(path.dirname(indexPath), path.join(outDir, entry.file))
|
|
357
|
-
.split(path.sep)
|
|
358
|
-
.join('/');
|
|
359
|
-
return `/// <reference path="${rel}" />`;
|
|
360
|
-
});
|
|
361
|
-
const content = references.length > 0
|
|
362
|
-
? `${header}
|
|
363
|
-
${references.join('\n')}
|
|
364
|
-
`
|
|
365
|
-
: `${header}
|
|
366
|
-
`;
|
|
367
|
-
await fs.writeFile(indexPath, content, 'utf8');
|
|
368
|
-
}
|
|
369
327
|
function formatErrorMessage(error) {
|
|
370
328
|
if (error instanceof Error && typeof error.message === 'string') {
|
|
371
329
|
return error.message;
|
|
@@ -375,6 +333,23 @@ function formatErrorMessage(error) {
|
|
|
375
333
|
function relativeToRoot(filePath, rootDir) {
|
|
376
334
|
return path.relative(rootDir, filePath) || filePath;
|
|
377
335
|
}
|
|
336
|
+
function isWithinRoot(filePath, rootDir) {
|
|
337
|
+
const relative = path.relative(rootDir, filePath);
|
|
338
|
+
return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative));
|
|
339
|
+
}
|
|
340
|
+
async function ensureSelectorModule(resolvedPath, selectors, previousManifest, nextManifest) {
|
|
341
|
+
const manifestKey = buildSelectorModuleManifestKey(resolvedPath);
|
|
342
|
+
const targetPath = buildSelectorModulePath(resolvedPath);
|
|
343
|
+
const source = formatSelectorModuleSource(selectors);
|
|
344
|
+
const hash = hashContent(source);
|
|
345
|
+
const previousEntry = previousManifest[manifestKey];
|
|
346
|
+
const needsWrite = previousEntry?.hash !== hash || !(await fileExists(targetPath));
|
|
347
|
+
if (needsWrite) {
|
|
348
|
+
await fs.writeFile(targetPath, source, 'utf8');
|
|
349
|
+
}
|
|
350
|
+
nextManifest[manifestKey] = { file: targetPath, hash };
|
|
351
|
+
return needsWrite;
|
|
352
|
+
}
|
|
378
353
|
async function fileExists(target) {
|
|
379
354
|
try {
|
|
380
355
|
await fs.access(target);
|
|
@@ -498,7 +473,6 @@ export async function runGenerateTypesCli(argv = process.argv.slice(2)) {
|
|
|
498
473
|
rootDir: parsed.rootDir,
|
|
499
474
|
include: parsed.include,
|
|
500
475
|
outDir: parsed.outDir,
|
|
501
|
-
typesRoot: parsed.typesRoot,
|
|
502
476
|
stableNamespace: parsed.stableNamespace,
|
|
503
477
|
});
|
|
504
478
|
reportCliResult(result);
|
|
@@ -513,12 +487,11 @@ function parseCliArgs(argv) {
|
|
|
513
487
|
let rootDir = process.cwd();
|
|
514
488
|
const include = [];
|
|
515
489
|
let outDir;
|
|
516
|
-
let typesRoot;
|
|
517
490
|
let stableNamespace;
|
|
518
491
|
for (let i = 0; i < argv.length; i += 1) {
|
|
519
492
|
const arg = argv[i];
|
|
520
493
|
if (arg === '--help' || arg === '-h') {
|
|
521
|
-
return { rootDir, include, outDir,
|
|
494
|
+
return { rootDir, include, outDir, stableNamespace, help: true };
|
|
522
495
|
}
|
|
523
496
|
if (arg === '--root' || arg === '-r') {
|
|
524
497
|
const value = argv[++i];
|
|
@@ -544,14 +517,6 @@ function parseCliArgs(argv) {
|
|
|
544
517
|
outDir = value;
|
|
545
518
|
continue;
|
|
546
519
|
}
|
|
547
|
-
if (arg === '--types-root') {
|
|
548
|
-
const value = argv[++i];
|
|
549
|
-
if (!value) {
|
|
550
|
-
throw new Error('Missing value for --types-root');
|
|
551
|
-
}
|
|
552
|
-
typesRoot = value;
|
|
553
|
-
continue;
|
|
554
|
-
}
|
|
555
520
|
if (arg === '--stable-namespace') {
|
|
556
521
|
const value = argv[++i];
|
|
557
522
|
if (!value) {
|
|
@@ -565,7 +530,7 @@ function parseCliArgs(argv) {
|
|
|
565
530
|
}
|
|
566
531
|
include.push(arg);
|
|
567
532
|
}
|
|
568
|
-
return { rootDir, include, outDir,
|
|
533
|
+
return { rootDir, include, outDir, stableNamespace };
|
|
569
534
|
}
|
|
570
535
|
function printHelp() {
|
|
571
536
|
console.log(`Usage: knighted-css-generate-types [options]
|
|
@@ -573,20 +538,19 @@ function printHelp() {
|
|
|
573
538
|
Options:
|
|
574
539
|
-r, --root <path> Project root directory (default: cwd)
|
|
575
540
|
-i, --include <path> Additional directories/files to scan (repeatable)
|
|
576
|
-
--out-dir <path>
|
|
577
|
-
--types-root <path> Directory for generated @types entrypoint
|
|
541
|
+
--out-dir <path> Directory to store selector module manifest cache
|
|
578
542
|
--stable-namespace <name> Stable namespace prefix for generated selector maps
|
|
579
543
|
-h, --help Show this help message
|
|
580
544
|
`);
|
|
581
545
|
}
|
|
582
546
|
function reportCliResult(result) {
|
|
583
|
-
if (result.
|
|
584
|
-
console.log('[knighted-css]
|
|
547
|
+
if (result.selectorModulesWritten === 0 && result.selectorModulesRemoved === 0) {
|
|
548
|
+
console.log('[knighted-css] Selector modules are up to date.');
|
|
585
549
|
}
|
|
586
550
|
else {
|
|
587
|
-
console.log(`[knighted-css]
|
|
551
|
+
console.log(`[knighted-css] Selector modules updated: wrote ${result.selectorModulesWritten}, removed ${result.selectorModulesRemoved}.`);
|
|
588
552
|
}
|
|
589
|
-
console.log(`[knighted-css]
|
|
553
|
+
console.log(`[knighted-css] Manifest: ${result.manifestPath}`);
|
|
590
554
|
for (const warning of result.warnings) {
|
|
591
555
|
console.warn(`[knighted-css] ${warning}`);
|
|
592
556
|
}
|
|
@@ -601,15 +565,12 @@ function setImportMetaUrlProvider(provider) {
|
|
|
601
565
|
importMetaUrlProvider = provider ?? getImportMetaUrl;
|
|
602
566
|
}
|
|
603
567
|
export const __generateTypesInternals = {
|
|
604
|
-
writeTypesIndex,
|
|
605
568
|
stripInlineLoader,
|
|
606
569
|
splitResourceAndQuery,
|
|
570
|
+
extractSelectorSourceSpecifier,
|
|
607
571
|
findSpecifierImports,
|
|
608
572
|
resolveImportPath,
|
|
609
573
|
resolvePackageRoot,
|
|
610
|
-
buildDeclarationFileName,
|
|
611
|
-
formatModuleDeclaration,
|
|
612
|
-
formatSelectorType,
|
|
613
574
|
relativeToRoot,
|
|
614
575
|
collectCandidateFiles,
|
|
615
576
|
normalizeIncludeOptions,
|
|
@@ -625,4 +586,11 @@ export const __generateTypesInternals = {
|
|
|
625
586
|
parseCliArgs,
|
|
626
587
|
printHelp,
|
|
627
588
|
reportCliResult,
|
|
589
|
+
buildSelectorModuleManifestKey,
|
|
590
|
+
buildSelectorModulePath,
|
|
591
|
+
formatSelectorModuleSource,
|
|
592
|
+
ensureSelectorModule,
|
|
593
|
+
removeStaleSelectorModules,
|
|
594
|
+
readManifest,
|
|
595
|
+
writeManifest,
|
|
628
596
|
};
|
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.12",
|
|
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",
|