@knighted/css 1.1.1 → 1.2.0-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -58,6 +58,7 @@ function getImportMetaUrl() {
58
58
  }
59
59
  const SELECTOR_REFERENCE = '.knighted-css';
60
60
  const SELECTOR_MODULE_SUFFIX = '.knighted-css.ts';
61
+ const DECLARATION_SUFFIX = '.d.ts';
61
62
  const STYLE_EXTENSIONS = DEFAULT_EXTENSIONS.map(ext => ext.toLowerCase());
62
63
  const SCRIPT_EXTENSIONS = Array.from(SUPPORTED_EXTENSIONS);
63
64
  const RESOLUTION_EXTENSIONS = Array.from(new Set([...SCRIPT_EXTENSIONS, ...STYLE_EXTENSIONS]));
@@ -67,11 +68,16 @@ const EXTENSION_FALLBACKS = {
67
68
  '.cjs': ['.cts', '.cjs', '.js', '.ts', '.tsx'],
68
69
  '.jsx': ['.tsx', '.jsx'],
69
70
  };
71
+ const DECLARATION_MODE_WARNING = 'Declaration mode requires a resolver plugin to append ?knighted-css (and &combined when applicable) so runtime exports match the generated types.';
70
72
  export async function generateTypes(options = {}) {
71
73
  const rootDir = await resolveRootDir(path.resolve(options.rootDir ?? process.cwd()));
72
74
  const include = normalizeIncludeOptions(options.include, rootDir);
73
75
  const cacheDir = path.resolve(options.outDir ?? path.join(rootDir, '.knighted-css'));
74
76
  const tsconfig = loadTsconfigResolutionContext(rootDir);
77
+ const mode = options.mode ?? 'module';
78
+ const manifestPath = options.manifestPath
79
+ ? path.resolve(rootDir, options.manifestPath)
80
+ : undefined;
75
81
  await fs.mkdir(cacheDir, { recursive: true });
76
82
  const internalOptions = {
77
83
  rootDir,
@@ -82,6 +88,8 @@ export async function generateTypes(options = {}) {
82
88
  hashed: options.hashed,
83
89
  tsconfig,
84
90
  resolver: options.resolver,
91
+ mode,
92
+ manifestPath,
85
93
  };
86
94
  return generateDeclarations(internalOptions);
87
95
  }
@@ -100,79 +108,176 @@ async function generateDeclarations(options) {
100
108
  const selectorModulesManifestPath = path.join(options.cacheDir, 'selector-modules.json');
101
109
  const previousSelectorManifest = await readManifest(selectorModulesManifestPath);
102
110
  const nextSelectorManifest = {};
111
+ const sidecarManifest = {};
103
112
  const selectorCache = new Map();
104
113
  const processedSelectors = new Set();
105
114
  const proxyInfoCache = new Map();
106
115
  const warnings = [];
107
116
  let selectorModuleWrites = 0;
108
- for (const filePath of files) {
109
- const matches = await findSpecifierImports(filePath);
110
- for (const match of matches) {
111
- const cleaned = match.specifier.trim();
112
- const inlineFree = stripInlineLoader(cleaned);
113
- const { resource } = splitResourceAndQuery(inlineFree);
114
- const selectorSource = extractSelectorSourceSpecifier(resource);
115
- if (!selectorSource) {
117
+ if (options.mode === 'declaration') {
118
+ warnings.push(DECLARATION_MODE_WARNING);
119
+ }
120
+ if (options.mode === 'declaration') {
121
+ for (const filePath of files) {
122
+ if (!isScriptResource(filePath)) {
116
123
  continue;
117
124
  }
118
- const resolvedNamespace = resolveStableNamespace(options.stableNamespace);
119
- const resolvedPath = await resolveImportPath(selectorSource, match.importer, options.rootDir, options.tsconfig, options.resolver, resolverFactory, RESOLUTION_EXTENSIONS);
120
- if (!resolvedPath) {
121
- warnings.push(`Unable to resolve ${selectorSource} referenced by ${relativeToRoot(match.importer, options.rootDir)}.`);
125
+ if (!isWithinRoot(filePath, options.rootDir)) {
126
+ warnings.push(`Skipping declaration output for ${relativeToRoot(filePath, options.rootDir)} because it is outside the project root.`);
127
+ continue;
128
+ }
129
+ const manifestKey = buildSelectorModuleManifestKey(filePath);
130
+ if (processedSelectors.has(manifestKey)) {
131
+ continue;
132
+ }
133
+ const hasStyles = await hasStyleImports(filePath, {
134
+ rootDir: options.rootDir,
135
+ tsconfig: options.tsconfig,
136
+ resolver: options.resolver,
137
+ resolverFactory,
138
+ });
139
+ if (!hasStyles) {
140
+ processedSelectors.add(manifestKey);
122
141
  continue;
123
142
  }
124
- const cacheKey = `${resolvedPath}::${resolvedNamespace}`;
143
+ const resolvedNamespace = resolveStableNamespace(options.stableNamespace);
144
+ const cacheKey = `${filePath}::${resolvedNamespace}::declaration`;
125
145
  let selectorMap = selectorCache.get(cacheKey);
126
146
  if (!selectorMap) {
127
147
  try {
128
- const shouldUseCssModules = resolvedPath.endsWith('.module.css');
129
- const { css } = await activeCssWithMeta(resolvedPath, {
148
+ let cssResult = await activeCssWithMeta(filePath, {
130
149
  cwd: options.rootDir,
131
150
  peerResolver,
132
151
  autoStable: options.autoStable ? { namespace: resolvedNamespace } : undefined,
133
- lightningcss: options.autoStable && shouldUseCssModules
134
- ? { cssModules: true }
135
- : undefined,
136
152
  resolver: options.resolver,
137
153
  });
154
+ if (cssResult.files.length === 0 || cssResult.css.trim().length === 0) {
155
+ processedSelectors.add(manifestKey);
156
+ continue;
157
+ }
158
+ if (options.autoStable &&
159
+ cssResult.files.some(file => isCssModuleResource(file))) {
160
+ cssResult = await activeCssWithMeta(filePath, {
161
+ cwd: options.rootDir,
162
+ peerResolver,
163
+ autoStable: options.autoStable
164
+ ? { namespace: resolvedNamespace }
165
+ : undefined,
166
+ lightningcss: { cssModules: true },
167
+ resolver: options.resolver,
168
+ });
169
+ }
138
170
  selectorMap = options.hashed
139
- ? collectSelectorTokensFromCss(css)
171
+ ? collectSelectorTokensFromCss(cssResult.css)
140
172
  : buildStableSelectorsLiteral({
141
- css,
173
+ css: cssResult.css,
142
174
  namespace: resolvedNamespace,
143
- resourcePath: resolvedPath,
175
+ resourcePath: filePath,
144
176
  emitWarning: message => warnings.push(message),
145
177
  }).selectorMap;
146
178
  }
147
179
  catch (error) {
148
- warnings.push(`Failed to extract CSS for ${relativeToRoot(resolvedPath, options.rootDir)}: ${formatErrorMessage(error)}`);
180
+ warnings.push(`Failed to extract CSS for ${relativeToRoot(filePath, options.rootDir)}: ${formatErrorMessage(error)}`);
181
+ processedSelectors.add(manifestKey);
149
182
  continue;
150
183
  }
151
184
  selectorCache.set(cacheKey, selectorMap);
152
185
  }
153
- if (!isWithinRoot(resolvedPath, options.rootDir)) {
154
- warnings.push(`Skipping selector module for ${relativeToRoot(resolvedPath, options.rootDir)} because it is outside the project root.`);
186
+ if (!selectorMap || selectorMap.size === 0) {
187
+ processedSelectors.add(manifestKey);
155
188
  continue;
156
189
  }
157
- const manifestKey = buildSelectorModuleManifestKey(resolvedPath);
158
- if (processedSelectors.has(manifestKey)) {
190
+ const proxyInfo = await resolveDeclarationProxyInfo(manifestKey, filePath, proxyInfoCache);
191
+ if (!proxyInfo) {
192
+ processedSelectors.add(manifestKey);
159
193
  continue;
160
194
  }
161
- const proxyInfo = await resolveProxyInfo(manifestKey, selectorSource, resolvedPath, proxyInfoCache);
162
- const moduleWrite = await ensureSelectorModule(resolvedPath, selectorMap, previousSelectorManifest, nextSelectorManifest, selectorSource, proxyInfo ?? undefined, options.hashed ?? false);
195
+ const moduleWrite = await ensureDeclarationModule(filePath, selectorMap, previousSelectorManifest, nextSelectorManifest, proxyInfo, options.hashed ?? false);
196
+ if (options.manifestPath) {
197
+ sidecarManifest[manifestKey] = { file: buildDeclarationPath(filePath) };
198
+ }
163
199
  if (moduleWrite) {
164
200
  selectorModuleWrites += 1;
165
201
  }
166
202
  processedSelectors.add(manifestKey);
167
203
  }
168
204
  }
205
+ else {
206
+ for (const filePath of files) {
207
+ const matches = await findSpecifierImports(filePath);
208
+ for (const match of matches) {
209
+ const cleaned = match.specifier.trim();
210
+ const inlineFree = stripInlineLoader(cleaned);
211
+ const { resource } = splitResourceAndQuery(inlineFree);
212
+ const selectorSource = extractSelectorSourceSpecifier(resource);
213
+ if (!selectorSource) {
214
+ continue;
215
+ }
216
+ const resolvedNamespace = resolveStableNamespace(options.stableNamespace);
217
+ const resolvedPath = await resolveImportPath(selectorSource, match.importer, options.rootDir, options.tsconfig, options.resolver, resolverFactory, RESOLUTION_EXTENSIONS);
218
+ if (!resolvedPath) {
219
+ warnings.push(`Unable to resolve ${selectorSource} referenced by ${relativeToRoot(match.importer, options.rootDir)}.`);
220
+ continue;
221
+ }
222
+ const cacheKey = `${resolvedPath}::${resolvedNamespace}`;
223
+ let selectorMap = selectorCache.get(cacheKey);
224
+ if (!selectorMap) {
225
+ try {
226
+ const shouldUseCssModules = resolvedPath.endsWith('.module.css');
227
+ const { css } = await activeCssWithMeta(resolvedPath, {
228
+ cwd: options.rootDir,
229
+ peerResolver,
230
+ autoStable: options.autoStable
231
+ ? { namespace: resolvedNamespace }
232
+ : undefined,
233
+ lightningcss: options.autoStable && shouldUseCssModules
234
+ ? { cssModules: true }
235
+ : undefined,
236
+ resolver: options.resolver,
237
+ });
238
+ selectorMap = options.hashed
239
+ ? collectSelectorTokensFromCss(css)
240
+ : buildStableSelectorsLiteral({
241
+ css,
242
+ namespace: resolvedNamespace,
243
+ resourcePath: resolvedPath,
244
+ emitWarning: message => warnings.push(message),
245
+ }).selectorMap;
246
+ }
247
+ catch (error) {
248
+ warnings.push(`Failed to extract CSS for ${relativeToRoot(resolvedPath, options.rootDir)}: ${formatErrorMessage(error)}`);
249
+ continue;
250
+ }
251
+ selectorCache.set(cacheKey, selectorMap);
252
+ }
253
+ if (!isWithinRoot(resolvedPath, options.rootDir)) {
254
+ warnings.push(`Skipping selector module for ${relativeToRoot(resolvedPath, options.rootDir)} because it is outside the project root.`);
255
+ continue;
256
+ }
257
+ const manifestKey = buildSelectorModuleManifestKey(resolvedPath);
258
+ if (processedSelectors.has(manifestKey)) {
259
+ continue;
260
+ }
261
+ const proxyInfo = await resolveProxyInfo(manifestKey, selectorSource, resolvedPath, proxyInfoCache);
262
+ const moduleWrite = await ensureSelectorModule(resolvedPath, selectorMap, previousSelectorManifest, nextSelectorManifest, selectorSource, proxyInfo ?? undefined, options.hashed ?? false);
263
+ if (moduleWrite) {
264
+ selectorModuleWrites += 1;
265
+ }
266
+ processedSelectors.add(manifestKey);
267
+ }
268
+ }
269
+ }
169
270
  const selectorModulesRemoved = await removeStaleSelectorModules(previousSelectorManifest, nextSelectorManifest);
170
271
  await writeManifest(selectorModulesManifestPath, nextSelectorManifest);
272
+ if (options.manifestPath && options.mode === 'declaration') {
273
+ await writeSidecarManifest(options.manifestPath, sidecarManifest);
274
+ }
171
275
  return {
172
276
  selectorModulesWritten: selectorModuleWrites,
173
277
  selectorModulesRemoved,
174
278
  warnings,
175
279
  manifestPath: selectorModulesManifestPath,
280
+ sidecarManifestPath: options.mode === 'declaration' ? options.manifestPath : undefined,
176
281
  };
177
282
  }
178
283
  function normalizeIncludeOptions(include, rootDir) {
@@ -351,6 +456,50 @@ function buildSelectorModulePath(resolvedPath) {
351
456
  const base = ext ? resolvedPath.slice(0, -ext.length) : resolvedPath;
352
457
  return `${base}${SELECTOR_MODULE_SUFFIX}`;
353
458
  }
459
+ function buildDeclarationModuleSpecifier(resolvedPath) {
460
+ const ext = path.extname(resolvedPath).toLowerCase();
461
+ const baseName = path.basename(resolvedPath, ext);
462
+ const mappedExt = ext === '.mjs' || ext === '.mts'
463
+ ? '.mjs'
464
+ : ext === '.cjs' || ext === '.cts'
465
+ ? '.cjs'
466
+ : '.js';
467
+ return `./${baseName}${mappedExt}`;
468
+ }
469
+ function buildDeclarationPath(resolvedPath) {
470
+ if (resolvedPath.endsWith(DECLARATION_SUFFIX)) {
471
+ return resolvedPath;
472
+ }
473
+ return `${resolvedPath}${DECLARATION_SUFFIX}`;
474
+ }
475
+ function formatSelectorTypeLiteral(selectors) {
476
+ const entries = Array.from(selectors.keys()).sort((a, b) => a.localeCompare(b));
477
+ const typeLines = entries.map(token => ` readonly ${JSON.stringify(token)}: string`);
478
+ return typeLines.length > 0
479
+ ? `{
480
+ ${typeLines.join('\n')}
481
+ }`
482
+ : 'Record<string, string>';
483
+ }
484
+ function formatDeclarationSource(selectors, proxyInfo, options = {}) {
485
+ const header = '// Generated by @knighted/css/generate-types\n// Do not edit.';
486
+ const isHashed = options.hashed === true;
487
+ const marker = isHashed ? '// @knighted-css:hashed' : '// @knighted-css';
488
+ const exportName = isHashed ? 'selectors' : 'stableSelectors';
489
+ const typeLiteral = formatSelectorTypeLiteral(selectors);
490
+ const shouldEmit = (name) => !proxyInfo.exportedNames?.has(name);
491
+ const lines = [
492
+ header,
493
+ marker,
494
+ '',
495
+ `declare module '${proxyInfo.moduleSpecifier}' {`,
496
+ shouldEmit('knightedCss') ? ' export const knightedCss: string' : '',
497
+ shouldEmit(exportName) ? ` export const ${exportName}: ${typeLiteral}` : '',
498
+ '}',
499
+ 'export {}',
500
+ ].filter(Boolean);
501
+ return `${lines.join('\n')}\n`;
502
+ }
354
503
  function formatSelectorModuleSource(selectors, proxyInfo, options = {}) {
355
504
  const header = '// Generated by @knighted/css/generate-types\n// Do not edit.';
356
505
  const entries = Array.from(selectors.entries()).sort(([a], [b]) => a.localeCompare(b));
@@ -428,6 +577,10 @@ async function readManifest(manifestPath) {
428
577
  async function writeManifest(manifestPath, manifest) {
429
578
  await fs.writeFile(manifestPath, JSON.stringify(manifest, null, 2), 'utf8');
430
579
  }
580
+ async function writeSidecarManifest(manifestPath, manifest) {
581
+ await fs.mkdir(path.dirname(manifestPath), { recursive: true });
582
+ await fs.writeFile(manifestPath, JSON.stringify(manifest, null, 2), 'utf8');
583
+ }
431
584
  async function removeStaleSelectorModules(previous, next) {
432
585
  const stale = Object.entries(previous).filter(([key]) => !next[key]);
433
586
  let removed = 0;
@@ -472,6 +625,19 @@ async function ensureSelectorModule(resolvedPath, selectors, previousManifest, n
472
625
  nextManifest[manifestKey] = { file: targetPath, hash };
473
626
  return needsWrite;
474
627
  }
628
+ async function ensureDeclarationModule(resolvedPath, selectors, previousManifest, nextManifest, proxyInfo, hashed) {
629
+ const manifestKey = buildSelectorModuleManifestKey(resolvedPath);
630
+ const targetPath = buildDeclarationPath(resolvedPath);
631
+ const source = formatDeclarationSource(selectors, proxyInfo, { hashed });
632
+ const hash = hashContent(source);
633
+ const previousEntry = previousManifest[manifestKey];
634
+ const needsWrite = previousEntry?.hash !== hash || !(await fileExists(targetPath));
635
+ if (needsWrite) {
636
+ await fs.writeFile(targetPath, source, 'utf8');
637
+ }
638
+ nextManifest[manifestKey] = { file: targetPath, hash };
639
+ return needsWrite;
640
+ }
475
641
  async function fileExists(target) {
476
642
  try {
477
643
  await fs.access(target);
@@ -613,6 +779,60 @@ function isStyleResource(filePath) {
613
779
  const normalized = filePath.toLowerCase();
614
780
  return STYLE_EXTENSIONS.some(ext => normalized.endsWith(ext));
615
781
  }
782
+ function isCssModuleResource(filePath) {
783
+ return /\.module\.(css|scss|sass|less)$/i.test(filePath);
784
+ }
785
+ function isScriptResource(filePath) {
786
+ const normalized = filePath.toLowerCase();
787
+ if (normalized.endsWith('.d.ts')) {
788
+ return false;
789
+ }
790
+ return SCRIPT_EXTENSIONS.some(ext => normalized.endsWith(ext));
791
+ }
792
+ async function hasStyleImports(filePath, options) {
793
+ let source;
794
+ try {
795
+ source = await fs.readFile(filePath, 'utf8');
796
+ }
797
+ catch {
798
+ return false;
799
+ }
800
+ const candidates = new Set();
801
+ try {
802
+ const analysis = await analyzeModule(source, filePath);
803
+ for (const specifier of analysis.imports) {
804
+ if (specifier) {
805
+ candidates.add(specifier);
806
+ }
807
+ }
808
+ }
809
+ catch {
810
+ // fall back to regex scanning below
811
+ }
812
+ const importRegex = /(import|require)\s*(?:\(|[^'"`]*)(['"])([^'"`]+)\2/g;
813
+ let match;
814
+ while ((match = importRegex.exec(source)) !== null) {
815
+ const specifier = match[3];
816
+ if (specifier) {
817
+ candidates.add(specifier);
818
+ }
819
+ }
820
+ for (const specifier of candidates) {
821
+ const cleaned = stripInlineLoader(specifier.trim());
822
+ const { resource } = splitResourceAndQuery(cleaned);
823
+ if (!resource) {
824
+ continue;
825
+ }
826
+ if (isStyleResource(resource)) {
827
+ return true;
828
+ }
829
+ const resolved = await resolveImportPath(resource, filePath, options.rootDir, options.tsconfig, options.resolver, options.resolverFactory, RESOLUTION_EXTENSIONS);
830
+ if (resolved && isStyleResource(resolved)) {
831
+ return true;
832
+ }
833
+ }
834
+ return false;
835
+ }
616
836
  function collectSelectorTokensFromCss(css) {
617
837
  const tokens = new Set();
618
838
  const pattern = /\.([A-Za-z_-][A-Za-z0-9_-]*)\b/g;
@@ -645,6 +865,21 @@ async function resolveProxyInfo(manifestKey, selectorSource, resolvedPath, cache
645
865
  cache.set(manifestKey, proxyInfo);
646
866
  return proxyInfo;
647
867
  }
868
+ async function resolveDeclarationProxyInfo(manifestKey, resolvedPath, cache) {
869
+ const cached = cache.get(manifestKey);
870
+ if (cached !== undefined) {
871
+ return cached;
872
+ }
873
+ const defaultSignal = await getDefaultExportSignal(resolvedPath);
874
+ const exportedNames = await getNamedExports(resolvedPath);
875
+ const proxyInfo = {
876
+ moduleSpecifier: buildDeclarationModuleSpecifier(resolvedPath),
877
+ includeDefault: defaultSignal === 'has-default',
878
+ exportedNames,
879
+ };
880
+ cache.set(manifestKey, proxyInfo);
881
+ return proxyInfo;
882
+ }
648
883
  function buildProxyModuleSpecifier(resolvedPath, selectorSource) {
649
884
  const resolvedExt = path.extname(resolvedPath);
650
885
  const baseName = path.basename(resolvedPath, resolvedExt);
@@ -662,6 +897,16 @@ async function getDefaultExportSignal(filePath) {
662
897
  return 'unknown';
663
898
  }
664
899
  }
900
+ async function getNamedExports(filePath) {
901
+ try {
902
+ const source = await fs.readFile(filePath, 'utf8');
903
+ const analysis = await analyzeModule(source, filePath);
904
+ return new Set(analysis.exports ?? []);
905
+ }
906
+ catch {
907
+ return new Set();
908
+ }
909
+ }
665
910
  function createProjectPeerResolver(rootDir) {
666
911
  const resolver = getProjectRequire(rootDir);
667
912
  return async (name) => {
@@ -734,6 +979,8 @@ export async function runGenerateTypesCli(argv = process.argv.slice(2)) {
734
979
  autoStable: parsed.autoStable,
735
980
  hashed: parsed.hashed,
736
981
  resolver,
982
+ mode: parsed.mode,
983
+ manifestPath: parsed.manifestPath,
737
984
  });
738
985
  reportCliResult(result);
739
986
  }
@@ -751,10 +998,20 @@ function parseCliArgs(argv) {
751
998
  let autoStable = false;
752
999
  let hashed = false;
753
1000
  let resolver;
1001
+ let mode = 'module';
1002
+ let manifestPath;
754
1003
  for (let i = 0; i < argv.length; i += 1) {
755
1004
  const arg = argv[i];
756
1005
  if (arg === '--help' || arg === '-h') {
757
- return { rootDir, include, outDir, stableNamespace, autoStable, help: true };
1006
+ return {
1007
+ rootDir,
1008
+ include,
1009
+ outDir,
1010
+ stableNamespace,
1011
+ autoStable,
1012
+ mode,
1013
+ help: true,
1014
+ };
758
1015
  }
759
1016
  if (arg === '--auto-stable') {
760
1017
  autoStable = true;
@@ -788,6 +1045,14 @@ function parseCliArgs(argv) {
788
1045
  outDir = value;
789
1046
  continue;
790
1047
  }
1048
+ if (arg === '--manifest') {
1049
+ const value = argv[++i];
1050
+ if (!value) {
1051
+ throw new Error('Missing value for --manifest');
1052
+ }
1053
+ manifestPath = value;
1054
+ continue;
1055
+ }
791
1056
  if (arg === '--stable-namespace') {
792
1057
  const value = argv[++i];
793
1058
  if (!value) {
@@ -804,6 +1069,17 @@ function parseCliArgs(argv) {
804
1069
  resolver = value;
805
1070
  continue;
806
1071
  }
1072
+ if (arg === '--mode') {
1073
+ const value = argv[++i];
1074
+ if (!value) {
1075
+ throw new Error('Missing value for --mode');
1076
+ }
1077
+ if (value !== 'module' && value !== 'declaration') {
1078
+ throw new Error(`Unknown mode: ${value}`);
1079
+ }
1080
+ mode = value;
1081
+ continue;
1082
+ }
807
1083
  if (arg.startsWith('-')) {
808
1084
  throw new Error(`Unknown flag: ${arg}`);
809
1085
  }
@@ -812,20 +1088,35 @@ function parseCliArgs(argv) {
812
1088
  if (autoStable && hashed) {
813
1089
  throw new Error('Cannot combine --auto-stable with --hashed');
814
1090
  }
815
- return { rootDir, include, outDir, stableNamespace, autoStable, hashed, resolver };
1091
+ if (manifestPath && mode !== 'declaration') {
1092
+ throw new Error('Cannot use --manifest unless --mode is declaration');
1093
+ }
1094
+ return {
1095
+ rootDir,
1096
+ include,
1097
+ outDir,
1098
+ stableNamespace,
1099
+ autoStable,
1100
+ hashed,
1101
+ resolver,
1102
+ mode,
1103
+ manifestPath,
1104
+ };
816
1105
  }
817
1106
  function printHelp() {
818
1107
  console.log(`Usage: knighted-css-generate-types [options]
819
1108
 
820
1109
  Options:
821
- -r, --root <path> Project root directory (default: cwd)
822
- -i, --include <path> Additional directories/files to scan (repeatable)
823
- --out-dir <path> Directory to store selector module manifest cache
824
- --stable-namespace <name> Stable namespace prefix for generated selector maps
825
- --auto-stable Enable autoStable when extracting CSS for selectors
826
- --hashed Emit selectors backed by loader-bridge hashed modules
827
- --resolver <path> Path or package name exporting a CssResolver
828
- -h, --help Show this help message
1110
+ -r, --root <path> Project root directory (default: cwd)
1111
+ -i, --include <path> Additional directories/files to scan (repeatable)
1112
+ --out-dir <path> Directory to store selector module manifest cache
1113
+ --stable-namespace <name> Stable namespace prefix for generated selector maps
1114
+ --auto-stable Enable autoStable when extracting CSS for selectors
1115
+ --hashed Emit selectors backed by loader-bridge hashed modules
1116
+ --resolver <path> Path or package name exporting a CssResolver
1117
+ --mode <module|declaration> Emit selector modules (module) or declaration files (declaration)
1118
+ --manifest <path> Write a sidecar manifest (declaration mode only)
1119
+ -h, --help Show this help message
829
1120
  `);
830
1121
  }
831
1122
  function reportCliResult(result) {
@@ -836,6 +1127,9 @@ function reportCliResult(result) {
836
1127
  console.log(`[knighted-css] Selector modules updated: wrote ${result.selectorModulesWritten}, removed ${result.selectorModulesRemoved}.`);
837
1128
  }
838
1129
  console.log(`[knighted-css] Manifest: ${result.manifestPath}`);
1130
+ if (result.sidecarManifestPath) {
1131
+ console.log(`[knighted-css] Sidecar manifest: ${result.sidecarManifestPath}`);
1132
+ }
839
1133
  for (const warning of result.warnings) {
840
1134
  console.warn(`[knighted-css] ${warning}`);
841
1135
  }
@@ -865,23 +1159,31 @@ export const __generateTypesInternals = {
865
1159
  setImportMetaUrlProvider,
866
1160
  isNonRelativeSpecifier,
867
1161
  isStyleResource,
1162
+ isCssModuleResource,
868
1163
  resolveProxyInfo,
1164
+ resolveDeclarationProxyInfo,
869
1165
  resolveWithExtensionFallback,
870
1166
  resolveIndexFallback,
871
1167
  createProjectPeerResolver,
872
1168
  getProjectRequire,
873
1169
  loadTsconfigResolutionContext,
874
1170
  resolveWithTsconfigPaths,
1171
+ hasStyleImports,
875
1172
  loadResolverModule,
876
1173
  parseCliArgs,
877
1174
  printHelp,
878
1175
  reportCliResult,
879
1176
  buildSelectorModuleManifestKey,
880
1177
  buildSelectorModulePath,
1178
+ buildDeclarationModuleSpecifier,
881
1179
  formatSelectorModuleSource,
1180
+ buildDeclarationPath,
1181
+ formatDeclarationSource,
1182
+ ensureDeclarationModule,
882
1183
  ensureSelectorModule,
883
1184
  removeStaleSelectorModules,
884
1185
  readManifest,
885
1186
  writeManifest,
1187
+ writeSidecarManifest,
886
1188
  };
887
1189
  //# sourceMappingURL=generateTypes.js.map