@knighted/css 1.0.10 → 1.1.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -0
- package/dist/autoStableSelectors.d.ts +10 -0
- package/dist/autoStableSelectors.js +118 -0
- package/dist/autoStableSelectors.js.map +1 -0
- package/dist/cjs/autoStableSelectors.cjs +122 -0
- package/dist/cjs/autoStableSelectors.cjs.map +1 -0
- package/dist/cjs/autoStableSelectors.d.cts +10 -0
- package/dist/cjs/css.cjs +61 -10
- package/dist/cjs/css.cjs.map +1 -1
- package/dist/cjs/css.d.cts +6 -1
- package/dist/cjs/generateTypes.cjs +152 -16
- package/dist/cjs/generateTypes.cjs.map +1 -1
- package/dist/cjs/generateTypes.d.cts +12 -2
- package/dist/cjs/helpers.cjs +20 -6
- package/dist/cjs/helpers.cjs.map +1 -1
- package/dist/cjs/helpers.d.cts +2 -2
- package/dist/cjs/loader.cjs +89 -9
- package/dist/cjs/loader.cjs.map +1 -1
- package/dist/cjs/moduleGraph.cjs.map +1 -1
- package/dist/cjs/stableSelectorsLiteral.cjs +7 -4
- package/dist/cjs/stableSelectorsLiteral.cjs.map +1 -1
- package/dist/cjs/types.d.cts +9 -0
- package/dist/css.d.ts +6 -1
- package/dist/css.js +62 -11
- package/dist/css.js.map +1 -1
- package/dist/generateTypes.d.ts +12 -2
- package/dist/generateTypes.js +153 -17
- package/dist/generateTypes.js.map +1 -1
- package/dist/helpers.d.ts +2 -2
- package/dist/helpers.js +20 -6
- package/dist/helpers.js.map +1 -1
- package/dist/loader.js +90 -10
- package/dist/loader.js.map +1 -1
- package/dist/moduleGraph.js.map +1 -1
- package/dist/stableSelectorsLiteral.js +7 -4
- package/dist/stableSelectorsLiteral.js.map +1 -1
- package/dist/types.d.ts +9 -0
- package/package.json +1 -1
package/dist/cjs/css.d.cts
CHANGED
|
@@ -1,19 +1,23 @@
|
|
|
1
1
|
import { type TransformOptions as LightningTransformOptions } from 'lightningcss';
|
|
2
2
|
import { type SpecificitySelector, type SpecificityStrategy } from './helpers.cjs';
|
|
3
|
+
import { type AutoStableOption } from './autoStableSelectors.cjs';
|
|
3
4
|
import type { ModuleGraphOptions } from './moduleGraph.cjs';
|
|
4
5
|
import type { CssResolver } from './types.cjs';
|
|
6
|
+
export type { AutoStableOption } from './autoStableSelectors.cjs';
|
|
5
7
|
export type { CssResolver } from './types.cjs';
|
|
6
8
|
export type { ModuleGraphOptions } from './moduleGraph.cjs';
|
|
7
9
|
export declare const DEFAULT_EXTENSIONS: string[];
|
|
8
10
|
type LightningCssConfig = boolean | Partial<Omit<LightningTransformOptions<never>, 'code'>>;
|
|
9
11
|
type PeerLoader = (name: string) => Promise<unknown>;
|
|
12
|
+
type StrictLightningVisitor = Exclude<LightningTransformOptions<never>['visitor'], undefined>;
|
|
10
13
|
export interface CssOptions {
|
|
11
14
|
extensions?: string[];
|
|
12
15
|
cwd?: string;
|
|
13
16
|
filter?: (filePath: string) => boolean;
|
|
14
17
|
lightningcss?: LightningCssConfig;
|
|
18
|
+
autoStable?: AutoStableOption;
|
|
15
19
|
specificityBoost?: {
|
|
16
|
-
visitor?:
|
|
20
|
+
visitor?: StrictLightningVisitor;
|
|
17
21
|
strategy?: SpecificityStrategy;
|
|
18
22
|
match?: SpecificitySelector[];
|
|
19
23
|
};
|
|
@@ -31,6 +35,7 @@ export interface VanillaCompileResult {
|
|
|
31
35
|
export interface CssResult {
|
|
32
36
|
css: string;
|
|
33
37
|
files: string[];
|
|
38
|
+
exports?: Record<string, string | string[]>;
|
|
34
39
|
}
|
|
35
40
|
export declare function css(entry: string, options?: CssOptions): Promise<string>;
|
|
36
41
|
export declare function cssWithMeta(entry: string, options?: CssOptions): Promise<CssResult>;
|
|
@@ -65,6 +65,13 @@ function getImportMetaUrl() {
|
|
|
65
65
|
}
|
|
66
66
|
const SELECTOR_REFERENCE = '.knighted-css';
|
|
67
67
|
const SELECTOR_MODULE_SUFFIX = '.knighted-css.ts';
|
|
68
|
+
const STYLE_EXTENSIONS = css_js_1.DEFAULT_EXTENSIONS.map(ext => ext.toLowerCase());
|
|
69
|
+
const EXTENSION_FALLBACKS = {
|
|
70
|
+
'.js': ['.ts', '.tsx', '.jsx', '.mjs', '.cjs'],
|
|
71
|
+
'.mjs': ['.mts', '.mjs', '.js', '.ts', '.tsx'],
|
|
72
|
+
'.cjs': ['.cts', '.cjs', '.js', '.ts', '.tsx'],
|
|
73
|
+
'.jsx': ['.tsx', '.jsx'],
|
|
74
|
+
};
|
|
68
75
|
async function generateTypes(options = {}) {
|
|
69
76
|
const rootDir = node_path_1.default.resolve(options.rootDir ?? process.cwd());
|
|
70
77
|
const include = normalizeIncludeOptions(options.include, rootDir);
|
|
@@ -76,6 +83,7 @@ async function generateTypes(options = {}) {
|
|
|
76
83
|
include,
|
|
77
84
|
cacheDir,
|
|
78
85
|
stableNamespace: options.stableNamespace,
|
|
86
|
+
autoStable: options.autoStable,
|
|
79
87
|
tsconfig,
|
|
80
88
|
};
|
|
81
89
|
return generateDeclarations(internalOptions);
|
|
@@ -88,6 +96,7 @@ async function generateDeclarations(options) {
|
|
|
88
96
|
const nextSelectorManifest = {};
|
|
89
97
|
const selectorCache = new Map();
|
|
90
98
|
const processedSelectors = new Set();
|
|
99
|
+
const proxyInfoCache = new Map();
|
|
91
100
|
const warnings = [];
|
|
92
101
|
let selectorModuleWrites = 0;
|
|
93
102
|
for (const filePath of files) {
|
|
@@ -110,9 +119,14 @@ async function generateDeclarations(options) {
|
|
|
110
119
|
let selectorMap = selectorCache.get(cacheKey);
|
|
111
120
|
if (!selectorMap) {
|
|
112
121
|
try {
|
|
122
|
+
const shouldUseCssModules = resolvedPath.endsWith('.module.css');
|
|
113
123
|
const { css } = await activeCssWithMeta(resolvedPath, {
|
|
114
124
|
cwd: options.rootDir,
|
|
115
125
|
peerResolver,
|
|
126
|
+
autoStable: options.autoStable ? { namespace: resolvedNamespace } : undefined,
|
|
127
|
+
lightningcss: options.autoStable && shouldUseCssModules
|
|
128
|
+
? { cssModules: true }
|
|
129
|
+
: undefined,
|
|
116
130
|
});
|
|
117
131
|
selectorMap = (0, stableSelectorsLiteral_js_1.buildStableSelectorsLiteral)({
|
|
118
132
|
css,
|
|
@@ -135,7 +149,8 @@ async function generateDeclarations(options) {
|
|
|
135
149
|
if (processedSelectors.has(manifestKey)) {
|
|
136
150
|
continue;
|
|
137
151
|
}
|
|
138
|
-
const
|
|
152
|
+
const proxyInfo = await resolveProxyInfo(manifestKey, selectorSource, resolvedPath, proxyInfoCache);
|
|
153
|
+
const moduleWrite = await ensureSelectorModule(resolvedPath, selectorMap, previousSelectorManifest, nextSelectorManifest, proxyInfo ?? undefined);
|
|
139
154
|
if (moduleWrite) {
|
|
140
155
|
selectorModuleWrites += 1;
|
|
141
156
|
}
|
|
@@ -257,6 +272,13 @@ function extractSelectorSourceSpecifier(specifier) {
|
|
|
257
272
|
if (!base) {
|
|
258
273
|
return undefined;
|
|
259
274
|
}
|
|
275
|
+
/**
|
|
276
|
+
* Handles specifiers like "./entry.knighted-css.ts" where the base has no
|
|
277
|
+
* extension but the selector suffix includes one.
|
|
278
|
+
*/
|
|
279
|
+
if (suffix && !node_path_1.default.extname(base)) {
|
|
280
|
+
return `${base}${suffix}`;
|
|
281
|
+
}
|
|
260
282
|
return base;
|
|
261
283
|
}
|
|
262
284
|
const projectRequireCache = new Map();
|
|
@@ -264,14 +286,14 @@ async function resolveImportPath(resourceSpecifier, importerPath, rootDir, tscon
|
|
|
264
286
|
if (!resourceSpecifier)
|
|
265
287
|
return undefined;
|
|
266
288
|
if (resourceSpecifier.startsWith('.')) {
|
|
267
|
-
return node_path_1.default.resolve(node_path_1.default.dirname(importerPath), resourceSpecifier);
|
|
289
|
+
return resolveWithExtensionFallback(node_path_1.default.resolve(node_path_1.default.dirname(importerPath), resourceSpecifier));
|
|
268
290
|
}
|
|
269
291
|
if (resourceSpecifier.startsWith('/')) {
|
|
270
|
-
return node_path_1.default.resolve(rootDir, resourceSpecifier.slice(1));
|
|
292
|
+
return resolveWithExtensionFallback(node_path_1.default.resolve(rootDir, resourceSpecifier.slice(1)));
|
|
271
293
|
}
|
|
272
294
|
const tsconfigResolved = await resolveWithTsconfigPaths(resourceSpecifier, tsconfig);
|
|
273
295
|
if (tsconfigResolved) {
|
|
274
|
-
return tsconfigResolved;
|
|
296
|
+
return resolveWithExtensionFallback(tsconfigResolved);
|
|
275
297
|
}
|
|
276
298
|
const requireFromRoot = getProjectRequire(rootDir);
|
|
277
299
|
try {
|
|
@@ -285,10 +307,15 @@ function buildSelectorModuleManifestKey(resolvedPath) {
|
|
|
285
307
|
return resolvedPath.split(node_path_1.default.sep).join('/');
|
|
286
308
|
}
|
|
287
309
|
function buildSelectorModulePath(resolvedPath) {
|
|
288
|
-
|
|
310
|
+
if (isStyleResource(resolvedPath)) {
|
|
311
|
+
return `${resolvedPath}${SELECTOR_MODULE_SUFFIX}`;
|
|
312
|
+
}
|
|
313
|
+
const ext = node_path_1.default.extname(resolvedPath);
|
|
314
|
+
const base = ext ? resolvedPath.slice(0, -ext.length) : resolvedPath;
|
|
315
|
+
return `${base}${SELECTOR_MODULE_SUFFIX}`;
|
|
289
316
|
}
|
|
290
|
-
function formatSelectorModuleSource(selectors) {
|
|
291
|
-
const header = '// Generated by @knighted/css/generate-types\n// Do not edit.\n';
|
|
317
|
+
function formatSelectorModuleSource(selectors, proxyInfo) {
|
|
318
|
+
const header = '// Generated by @knighted/css/generate-types\n// Do not edit.\n\n';
|
|
292
319
|
const entries = Array.from(selectors.entries()).sort(([a], [b]) => a.localeCompare(b));
|
|
293
320
|
const lines = entries.map(([token, selector]) => ` ${JSON.stringify(token)}: ${JSON.stringify(selector)},`);
|
|
294
321
|
const literal = lines.length > 0
|
|
@@ -296,14 +323,21 @@ function formatSelectorModuleSource(selectors) {
|
|
|
296
323
|
${lines.join('\n')}
|
|
297
324
|
} as const`
|
|
298
325
|
: '{} as const';
|
|
299
|
-
|
|
326
|
+
const proxyLines = [];
|
|
327
|
+
if (proxyInfo) {
|
|
328
|
+
proxyLines.push(`export * from '${proxyInfo.moduleSpecifier}'`);
|
|
329
|
+
if (proxyInfo.includeDefault) {
|
|
330
|
+
proxyLines.push(`export { default } from '${proxyInfo.moduleSpecifier}'`);
|
|
331
|
+
}
|
|
332
|
+
proxyLines.push(`export { knightedCss } from '${proxyInfo.moduleSpecifier}?knighted-css'`);
|
|
333
|
+
proxyLines.push('');
|
|
334
|
+
}
|
|
335
|
+
const defaultExport = proxyInfo ? '' : '\nexport default stableSelectors\n';
|
|
336
|
+
return `${header}${proxyLines.join('\n')}
|
|
300
337
|
export const stableSelectors = ${literal}
|
|
301
338
|
|
|
302
339
|
export type KnightedCssStableSelectors = typeof stableSelectors
|
|
303
|
-
export type KnightedCssStableSelectorToken = keyof typeof stableSelectors
|
|
304
|
-
|
|
305
|
-
export default stableSelectors
|
|
306
|
-
`;
|
|
340
|
+
export type KnightedCssStableSelectorToken = keyof typeof stableSelectors${defaultExport}`;
|
|
307
341
|
}
|
|
308
342
|
function hashContent(content) {
|
|
309
343
|
return node_crypto_1.default.createHash('sha1').update(content).digest('hex');
|
|
@@ -347,10 +381,10 @@ function isWithinRoot(filePath, rootDir) {
|
|
|
347
381
|
const relative = node_path_1.default.relative(rootDir, filePath);
|
|
348
382
|
return relative === '' || (!relative.startsWith('..') && !node_path_1.default.isAbsolute(relative));
|
|
349
383
|
}
|
|
350
|
-
async function ensureSelectorModule(resolvedPath, selectors, previousManifest, nextManifest) {
|
|
384
|
+
async function ensureSelectorModule(resolvedPath, selectors, previousManifest, nextManifest, proxyInfo) {
|
|
351
385
|
const manifestKey = buildSelectorModuleManifestKey(resolvedPath);
|
|
352
386
|
const targetPath = buildSelectorModulePath(resolvedPath);
|
|
353
|
-
const source = formatSelectorModuleSource(selectors);
|
|
387
|
+
const source = formatSelectorModuleSource(selectors, proxyInfo);
|
|
354
388
|
const hash = hashContent(source);
|
|
355
389
|
const previousEntry = previousManifest[manifestKey];
|
|
356
390
|
const needsWrite = previousEntry?.hash !== hash || !(await fileExists(targetPath));
|
|
@@ -387,6 +421,62 @@ async function resolveWithTsconfigPaths(specifier, tsconfig) {
|
|
|
387
421
|
}
|
|
388
422
|
return undefined;
|
|
389
423
|
}
|
|
424
|
+
async function resolveWithExtensionFallback(candidatePath) {
|
|
425
|
+
try {
|
|
426
|
+
const stat = await promises_1.default.stat(candidatePath);
|
|
427
|
+
if (stat.isFile()) {
|
|
428
|
+
return candidatePath;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
catch {
|
|
432
|
+
// continue to resolution fallbacks
|
|
433
|
+
}
|
|
434
|
+
const ext = node_path_1.default.extname(candidatePath);
|
|
435
|
+
const base = ext ? candidatePath.slice(0, -ext.length) : candidatePath;
|
|
436
|
+
if (!ext) {
|
|
437
|
+
const resolved = await resolveWithExtensionList(base, Array.from(SUPPORTED_EXTENSIONS));
|
|
438
|
+
if (resolved) {
|
|
439
|
+
return resolved;
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
if (ext && EXTENSION_FALLBACKS[ext]) {
|
|
443
|
+
const resolved = await resolveWithExtensionList(base, EXTENSION_FALLBACKS[ext]);
|
|
444
|
+
if (resolved) {
|
|
445
|
+
return resolved;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
const indexResolved = await resolveIndexFallback(candidatePath);
|
|
449
|
+
if (indexResolved) {
|
|
450
|
+
return indexResolved;
|
|
451
|
+
}
|
|
452
|
+
/*
|
|
453
|
+
* Return the original candidate to preserve existing behavior when nothing
|
|
454
|
+
* resolves (callers may still want a best-effort path for warnings).
|
|
455
|
+
*/
|
|
456
|
+
return candidatePath;
|
|
457
|
+
}
|
|
458
|
+
async function resolveWithExtensionList(base, extensions) {
|
|
459
|
+
for (const extension of extensions) {
|
|
460
|
+
const candidate = `${base}${extension}`;
|
|
461
|
+
if (await fileExists(candidate)) {
|
|
462
|
+
return candidate;
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
return undefined;
|
|
466
|
+
}
|
|
467
|
+
async function resolveIndexFallback(candidatePath) {
|
|
468
|
+
try {
|
|
469
|
+
const stat = await promises_1.default.stat(candidatePath);
|
|
470
|
+
if (!stat.isDirectory()) {
|
|
471
|
+
return undefined;
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
catch {
|
|
475
|
+
return undefined;
|
|
476
|
+
}
|
|
477
|
+
const base = node_path_1.default.join(candidatePath, 'index');
|
|
478
|
+
return resolveWithExtensionList(base, Array.from(SUPPORTED_EXTENSIONS));
|
|
479
|
+
}
|
|
390
480
|
function loadTsconfigResolutionContext(rootDir, loader = get_tsconfig_1.getTsconfig) {
|
|
391
481
|
let result;
|
|
392
482
|
try {
|
|
@@ -441,6 +531,43 @@ function isNonRelativeSpecifier(specifier) {
|
|
|
441
531
|
}
|
|
442
532
|
return true;
|
|
443
533
|
}
|
|
534
|
+
function isStyleResource(filePath) {
|
|
535
|
+
const normalized = filePath.toLowerCase();
|
|
536
|
+
return STYLE_EXTENSIONS.some(ext => normalized.endsWith(ext));
|
|
537
|
+
}
|
|
538
|
+
async function resolveProxyInfo(manifestKey, selectorSource, resolvedPath, cache) {
|
|
539
|
+
if (isStyleResource(resolvedPath)) {
|
|
540
|
+
return null;
|
|
541
|
+
}
|
|
542
|
+
const cached = cache.get(manifestKey);
|
|
543
|
+
if (cached !== undefined) {
|
|
544
|
+
return cached;
|
|
545
|
+
}
|
|
546
|
+
const defaultSignal = await getDefaultExportSignal(resolvedPath);
|
|
547
|
+
const proxyInfo = {
|
|
548
|
+
moduleSpecifier: buildProxyModuleSpecifier(resolvedPath, selectorSource),
|
|
549
|
+
includeDefault: defaultSignal === 'has-default',
|
|
550
|
+
};
|
|
551
|
+
cache.set(manifestKey, proxyInfo);
|
|
552
|
+
return proxyInfo;
|
|
553
|
+
}
|
|
554
|
+
function buildProxyModuleSpecifier(resolvedPath, selectorSource) {
|
|
555
|
+
const resolvedExt = node_path_1.default.extname(resolvedPath);
|
|
556
|
+
const baseName = node_path_1.default.basename(resolvedPath, resolvedExt);
|
|
557
|
+
const selectorExt = node_path_1.default.extname(selectorSource);
|
|
558
|
+
const fileName = selectorExt ? `${baseName}${selectorExt}` : `${baseName}.js`;
|
|
559
|
+
return `./${fileName}`;
|
|
560
|
+
}
|
|
561
|
+
async function getDefaultExportSignal(filePath) {
|
|
562
|
+
try {
|
|
563
|
+
const source = await promises_1.default.readFile(filePath, 'utf8');
|
|
564
|
+
const analysis = await (0, lexer_js_1.analyzeModule)(source, filePath);
|
|
565
|
+
return analysis.defaultSignal;
|
|
566
|
+
}
|
|
567
|
+
catch {
|
|
568
|
+
return 'unknown';
|
|
569
|
+
}
|
|
570
|
+
}
|
|
444
571
|
function createProjectPeerResolver(rootDir) {
|
|
445
572
|
const resolver = getProjectRequire(rootDir);
|
|
446
573
|
return async (name) => {
|
|
@@ -484,6 +611,7 @@ async function runGenerateTypesCli(argv = process.argv.slice(2)) {
|
|
|
484
611
|
include: parsed.include,
|
|
485
612
|
outDir: parsed.outDir,
|
|
486
613
|
stableNamespace: parsed.stableNamespace,
|
|
614
|
+
autoStable: parsed.autoStable,
|
|
487
615
|
});
|
|
488
616
|
reportCliResult(result);
|
|
489
617
|
}
|
|
@@ -498,10 +626,15 @@ function parseCliArgs(argv) {
|
|
|
498
626
|
const include = [];
|
|
499
627
|
let outDir;
|
|
500
628
|
let stableNamespace;
|
|
629
|
+
let autoStable = false;
|
|
501
630
|
for (let i = 0; i < argv.length; i += 1) {
|
|
502
631
|
const arg = argv[i];
|
|
503
632
|
if (arg === '--help' || arg === '-h') {
|
|
504
|
-
return { rootDir, include, outDir, stableNamespace, help: true };
|
|
633
|
+
return { rootDir, include, outDir, stableNamespace, autoStable, help: true };
|
|
634
|
+
}
|
|
635
|
+
if (arg === '--auto-stable') {
|
|
636
|
+
autoStable = true;
|
|
637
|
+
continue;
|
|
505
638
|
}
|
|
506
639
|
if (arg === '--root' || arg === '-r') {
|
|
507
640
|
const value = argv[++i];
|
|
@@ -540,7 +673,7 @@ function parseCliArgs(argv) {
|
|
|
540
673
|
}
|
|
541
674
|
include.push(arg);
|
|
542
675
|
}
|
|
543
|
-
return { rootDir, include, outDir, stableNamespace };
|
|
676
|
+
return { rootDir, include, outDir, stableNamespace, autoStable };
|
|
544
677
|
}
|
|
545
678
|
function printHelp() {
|
|
546
679
|
console.log(`Usage: knighted-css-generate-types [options]
|
|
@@ -550,6 +683,7 @@ Options:
|
|
|
550
683
|
-i, --include <path> Additional directories/files to scan (repeatable)
|
|
551
684
|
--out-dir <path> Directory to store selector module manifest cache
|
|
552
685
|
--stable-namespace <name> Stable namespace prefix for generated selector maps
|
|
686
|
+
--auto-stable Enable autoStable when extracting CSS for selectors
|
|
553
687
|
-h, --help Show this help message
|
|
554
688
|
`);
|
|
555
689
|
}
|
|
@@ -589,6 +723,8 @@ exports.__generateTypesInternals = {
|
|
|
589
723
|
setModuleTypeDetector,
|
|
590
724
|
setImportMetaUrlProvider,
|
|
591
725
|
isNonRelativeSpecifier,
|
|
726
|
+
isStyleResource,
|
|
727
|
+
resolveWithExtensionFallback,
|
|
592
728
|
createProjectPeerResolver,
|
|
593
729
|
getProjectRequire,
|
|
594
730
|
loadTsconfigResolutionContext,
|