@knighted/css 1.0.0-rc.8 → 1.0.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.
- package/dist/cjs/css.cjs +19 -15
- package/dist/cjs/css.d.cts +5 -4
- package/dist/cjs/generateTypes.cjs +214 -139
- package/dist/cjs/generateTypes.d.cts +65 -16
- package/dist/cjs/loader-helpers.cjs +6 -0
- package/dist/cjs/loader-helpers.d.cts +4 -0
- package/dist/cjs/loader.cjs +2 -0
- package/dist/cjs/loader.d.cts +2 -1
- package/dist/cjs/moduleGraph.cjs +432 -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/stableSelectors.cjs +29 -0
- package/dist/cjs/stableSelectors.d.cts +15 -0
- package/dist/cjs/stableSelectorsLiteral.cjs +8 -5
- package/dist/cjs/stableSelectorsLiteral.d.cts +2 -0
- 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/generateTypes.d.ts +65 -16
- package/dist/generateTypes.js +214 -139
- package/dist/loader-helpers.d.ts +4 -0
- package/dist/loader-helpers.js +3 -0
- package/dist/loader.d.ts +2 -1
- package/dist/loader.js +2 -0
- package/dist/moduleGraph.d.ts +15 -0
- package/dist/moduleGraph.js +426 -0
- package/dist/sassInternals.d.ts +3 -4
- package/dist/sassInternals.js +6 -3
- package/dist/stableSelectors.d.ts +15 -0
- package/dist/stableSelectors.js +28 -0
- package/dist/stableSelectorsLiteral.d.ts +2 -0
- package/dist/stableSelectorsLiteral.js +8 -5
- package/dist/types.d.ts +4 -0
- package/dist/types.js +1 -0
- package/loader-queries.d.ts +6 -1
- package/package.json +14 -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
|
}
|
|
@@ -13,10 +13,12 @@ const node_module_1 = require("node:module");
|
|
|
13
13
|
const node_url_1 = require("node:url");
|
|
14
14
|
const es_module_lexer_1 = require("es-module-lexer");
|
|
15
15
|
const node_module_type_1 = require("node-module-type");
|
|
16
|
+
const get_tsconfig_1 = require("get-tsconfig");
|
|
17
|
+
const tsconfig_paths_1 = require("tsconfig-paths");
|
|
16
18
|
const css_js_1 = require("./css.cjs");
|
|
17
|
-
const loaderInternals_js_1 = require("./loaderInternals.cjs");
|
|
18
19
|
const stableSelectorsLiteral_js_1 = require("./stableSelectorsLiteral.cjs");
|
|
19
20
|
const stableNamespace_js_1 = require("./stableNamespace.cjs");
|
|
21
|
+
let activeCssWithMeta = css_js_1.cssWithMeta;
|
|
20
22
|
const DEFAULT_SKIP_DIRS = new Set([
|
|
21
23
|
'node_modules',
|
|
22
24
|
'.git',
|
|
@@ -40,12 +42,14 @@ const SUPPORTED_EXTENSIONS = new Set([
|
|
|
40
42
|
'.mjs',
|
|
41
43
|
'.cjs',
|
|
42
44
|
]);
|
|
45
|
+
let moduleTypeDetector = node_module_type_1.moduleType;
|
|
46
|
+
let importMetaUrlProvider = getImportMetaUrl;
|
|
43
47
|
function resolvePackageRoot() {
|
|
44
|
-
const detectedType = (
|
|
48
|
+
const detectedType = moduleTypeDetector();
|
|
45
49
|
if (detectedType === 'commonjs' && typeof __dirname === 'string') {
|
|
46
50
|
return node_path_1.default.resolve(__dirname, '..');
|
|
47
51
|
}
|
|
48
|
-
const moduleUrl =
|
|
52
|
+
const moduleUrl = importMetaUrlProvider();
|
|
49
53
|
if (moduleUrl) {
|
|
50
54
|
return node_path_1.default.resolve(node_path_1.default.dirname((0, node_url_1.fileURLToPath)(moduleUrl)), '..');
|
|
51
55
|
}
|
|
@@ -60,61 +64,55 @@ function getImportMetaUrl() {
|
|
|
60
64
|
}
|
|
61
65
|
}
|
|
62
66
|
const PACKAGE_ROOT = resolvePackageRoot();
|
|
63
|
-
const
|
|
64
|
-
const
|
|
67
|
+
const SELECTOR_REFERENCE = '.knighted-css';
|
|
68
|
+
const SELECTOR_MODULE_SUFFIX = '.knighted-css.ts';
|
|
65
69
|
async function generateTypes(options = {}) {
|
|
66
70
|
const rootDir = node_path_1.default.resolve(options.rootDir ?? process.cwd());
|
|
67
71
|
const include = normalizeIncludeOptions(options.include, rootDir);
|
|
68
|
-
const
|
|
69
|
-
const
|
|
72
|
+
const cacheDir = node_path_1.default.resolve(options.outDir ?? node_path_1.default.join(rootDir, '.knighted-css'));
|
|
73
|
+
const tsconfig = loadTsconfigResolutionContext(rootDir);
|
|
70
74
|
await es_module_lexer_1.init;
|
|
71
|
-
await promises_1.default.mkdir(
|
|
72
|
-
await promises_1.default.mkdir(typesRoot, { recursive: true });
|
|
75
|
+
await promises_1.default.mkdir(cacheDir, { recursive: true });
|
|
73
76
|
const internalOptions = {
|
|
74
77
|
rootDir,
|
|
75
78
|
include,
|
|
76
|
-
|
|
77
|
-
typesRoot,
|
|
79
|
+
cacheDir,
|
|
78
80
|
stableNamespace: options.stableNamespace,
|
|
81
|
+
tsconfig,
|
|
79
82
|
};
|
|
80
83
|
return generateDeclarations(internalOptions);
|
|
81
84
|
}
|
|
82
85
|
async function generateDeclarations(options) {
|
|
83
86
|
const peerResolver = createProjectPeerResolver(options.rootDir);
|
|
84
87
|
const files = await collectCandidateFiles(options.include);
|
|
85
|
-
const
|
|
86
|
-
const
|
|
87
|
-
const
|
|
88
|
+
const selectorModulesManifestPath = node_path_1.default.join(options.cacheDir, 'selector-modules.json');
|
|
89
|
+
const previousSelectorManifest = await readManifest(selectorModulesManifestPath);
|
|
90
|
+
const nextSelectorManifest = {};
|
|
88
91
|
const selectorCache = new Map();
|
|
89
|
-
const
|
|
90
|
-
const declarations = [];
|
|
92
|
+
const processedSelectors = new Set();
|
|
91
93
|
const warnings = [];
|
|
92
|
-
let
|
|
94
|
+
let selectorModuleWrites = 0;
|
|
93
95
|
for (const filePath of files) {
|
|
94
96
|
const matches = await findSpecifierImports(filePath);
|
|
95
97
|
for (const match of matches) {
|
|
96
98
|
const cleaned = match.specifier.trim();
|
|
97
99
|
const inlineFree = stripInlineLoader(cleaned);
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
if (!query || !(0, loaderInternals_js_1.hasQueryFlag)(query, loaderInternals_js_1.TYPES_QUERY_FLAG)) {
|
|
102
|
-
continue;
|
|
103
|
-
}
|
|
104
|
-
if (processedSpecifiers.has(cleaned)) {
|
|
100
|
+
const { resource } = splitResourceAndQuery(inlineFree);
|
|
101
|
+
const selectorSource = extractSelectorSourceSpecifier(resource);
|
|
102
|
+
if (!selectorSource) {
|
|
105
103
|
continue;
|
|
106
104
|
}
|
|
107
105
|
const resolvedNamespace = (0, stableNamespace_js_1.resolveStableNamespace)(options.stableNamespace);
|
|
108
|
-
const resolvedPath = await resolveImportPath(
|
|
106
|
+
const resolvedPath = await resolveImportPath(selectorSource, match.importer, options.rootDir, options.tsconfig);
|
|
109
107
|
if (!resolvedPath) {
|
|
110
|
-
warnings.push(`Unable to resolve ${
|
|
108
|
+
warnings.push(`Unable to resolve ${selectorSource} referenced by ${relativeToRoot(match.importer, options.rootDir)}.`);
|
|
111
109
|
continue;
|
|
112
110
|
}
|
|
113
111
|
const cacheKey = `${resolvedPath}::${resolvedNamespace}`;
|
|
114
112
|
let selectorMap = selectorCache.get(cacheKey);
|
|
115
113
|
if (!selectorMap) {
|
|
116
114
|
try {
|
|
117
|
-
const { css } = await (
|
|
115
|
+
const { css } = await activeCssWithMeta(resolvedPath, {
|
|
118
116
|
cwd: options.rootDir,
|
|
119
117
|
peerResolver,
|
|
120
118
|
});
|
|
@@ -131,38 +129,28 @@ async function generateDeclarations(options) {
|
|
|
131
129
|
}
|
|
132
130
|
selectorCache.set(cacheKey, selectorMap);
|
|
133
131
|
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
const
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
if (needsWrite) {
|
|
142
|
-
await promises_1.default.writeFile(targetPath, declaration, 'utf8');
|
|
143
|
-
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;
|
|
144
139
|
}
|
|
145
|
-
|
|
146
|
-
if (
|
|
147
|
-
|
|
140
|
+
const moduleWrite = await ensureSelectorModule(resolvedPath, selectorMap, previousSelectorManifest, nextSelectorManifest);
|
|
141
|
+
if (moduleWrite) {
|
|
142
|
+
selectorModuleWrites += 1;
|
|
148
143
|
}
|
|
149
|
-
|
|
144
|
+
processedSelectors.add(manifestKey);
|
|
150
145
|
}
|
|
151
146
|
}
|
|
152
|
-
const
|
|
153
|
-
await writeManifest(
|
|
154
|
-
const typesIndexPath = node_path_1.default.join(options.typesRoot, 'index.d.ts');
|
|
155
|
-
await writeTypesIndex(typesIndexPath, nextManifest, options.outDir);
|
|
156
|
-
if (Object.keys(nextManifest).length === 0) {
|
|
157
|
-
declarations.length = 0;
|
|
158
|
-
}
|
|
147
|
+
const selectorModulesRemoved = await removeStaleSelectorModules(previousSelectorManifest, nextSelectorManifest);
|
|
148
|
+
await writeManifest(selectorModulesManifestPath, nextSelectorManifest);
|
|
159
149
|
return {
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
declarations,
|
|
150
|
+
selectorModulesWritten: selectorModuleWrites,
|
|
151
|
+
selectorModulesRemoved,
|
|
163
152
|
warnings,
|
|
164
|
-
|
|
165
|
-
typesIndexPath,
|
|
153
|
+
manifestPath: selectorModulesManifestPath,
|
|
166
154
|
};
|
|
167
155
|
}
|
|
168
156
|
function normalizeIncludeOptions(include, rootDir) {
|
|
@@ -220,18 +208,18 @@ async function findSpecifierImports(filePath) {
|
|
|
220
208
|
catch {
|
|
221
209
|
return [];
|
|
222
210
|
}
|
|
223
|
-
if (!source.includes(
|
|
211
|
+
if (!source.includes(SELECTOR_REFERENCE)) {
|
|
224
212
|
return [];
|
|
225
213
|
}
|
|
226
214
|
const matches = [];
|
|
227
215
|
const [imports] = (0, es_module_lexer_1.parse)(source, filePath);
|
|
228
216
|
for (const record of imports) {
|
|
229
217
|
const specifier = record.n ?? source.slice(record.s, record.e);
|
|
230
|
-
if (specifier && specifier.includes(
|
|
218
|
+
if (specifier && specifier.includes(SELECTOR_REFERENCE)) {
|
|
231
219
|
matches.push({ specifier, importer: filePath });
|
|
232
220
|
}
|
|
233
221
|
}
|
|
234
|
-
const requireRegex = /require\((['"])([^'"`]
|
|
222
|
+
const requireRegex = /require\((['"])([^'"`]+?\.knighted-css[^'"`]*)\1\)/g;
|
|
235
223
|
let reqMatch;
|
|
236
224
|
while ((reqMatch = requireRegex.exec(source)) !== null) {
|
|
237
225
|
const spec = reqMatch[2];
|
|
@@ -254,8 +242,23 @@ function splitResourceAndQuery(specifier) {
|
|
|
254
242
|
}
|
|
255
243
|
return { resource: trimmed.slice(0, queryIndex), query: trimmed.slice(queryIndex) };
|
|
256
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
|
+
}
|
|
257
260
|
const projectRequireCache = new Map();
|
|
258
|
-
async function resolveImportPath(resourceSpecifier, importerPath, rootDir) {
|
|
261
|
+
async function resolveImportPath(resourceSpecifier, importerPath, rootDir, tsconfig) {
|
|
259
262
|
if (!resourceSpecifier)
|
|
260
263
|
return undefined;
|
|
261
264
|
if (resourceSpecifier.startsWith('.')) {
|
|
@@ -264,6 +267,10 @@ async function resolveImportPath(resourceSpecifier, importerPath, rootDir) {
|
|
|
264
267
|
if (resourceSpecifier.startsWith('/')) {
|
|
265
268
|
return node_path_1.default.resolve(rootDir, resourceSpecifier.slice(1));
|
|
266
269
|
}
|
|
270
|
+
const tsconfigResolved = await resolveWithTsconfigPaths(resourceSpecifier, tsconfig);
|
|
271
|
+
if (tsconfigResolved) {
|
|
272
|
+
return tsconfigResolved;
|
|
273
|
+
}
|
|
267
274
|
const requireFromRoot = getProjectRequire(rootDir);
|
|
268
275
|
try {
|
|
269
276
|
return requireFromRoot.resolve(resourceSpecifier);
|
|
@@ -272,47 +279,29 @@ async function resolveImportPath(resourceSpecifier, importerPath, rootDir) {
|
|
|
272
279
|
return undefined;
|
|
273
280
|
}
|
|
274
281
|
}
|
|
275
|
-
function
|
|
276
|
-
|
|
277
|
-
return `knt-${digest}.d.ts`;
|
|
278
|
-
}
|
|
279
|
-
function formatModuleDeclaration(specifier, variant, selectors) {
|
|
280
|
-
const literalSpecifier = JSON.stringify(specifier);
|
|
281
|
-
const selectorType = formatSelectorType(selectors);
|
|
282
|
-
const header = `declare module ${literalSpecifier} {`;
|
|
283
|
-
const footer = '}';
|
|
284
|
-
if (variant === 'types') {
|
|
285
|
-
return `${header}
|
|
286
|
-
export const knightedCss: string
|
|
287
|
-
export const stableSelectors: ${selectorType}
|
|
288
|
-
${footer}
|
|
289
|
-
`;
|
|
290
|
-
}
|
|
291
|
-
const stableLine = ` export const stableSelectors: ${selectorType}`;
|
|
292
|
-
const shared = ` const combined: KnightedCssCombinedModule<Record<string, unknown>>
|
|
293
|
-
export const knightedCss: string
|
|
294
|
-
${stableLine}`;
|
|
295
|
-
if (variant === 'combined') {
|
|
296
|
-
return `${header}
|
|
297
|
-
${shared}
|
|
298
|
-
export default combined
|
|
299
|
-
${footer}
|
|
300
|
-
`;
|
|
301
|
-
}
|
|
302
|
-
return `${header}
|
|
303
|
-
${shared}
|
|
304
|
-
${footer}
|
|
305
|
-
`;
|
|
282
|
+
function buildSelectorModuleManifestKey(resolvedPath) {
|
|
283
|
+
return resolvedPath.split(node_path_1.default.sep).join('/');
|
|
306
284
|
}
|
|
307
|
-
function
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
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';
|
|
311
290
|
const entries = Array.from(selectors.entries()).sort(([a], [b]) => a.localeCompare(b));
|
|
312
|
-
const lines = entries.map(([token, selector]) => `
|
|
313
|
-
|
|
291
|
+
const lines = entries.map(([token, selector]) => ` ${JSON.stringify(token)}: ${JSON.stringify(selector)},`);
|
|
292
|
+
const literal = lines.length > 0
|
|
293
|
+
? `{
|
|
314
294
|
${lines.join('\n')}
|
|
315
|
-
|
|
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
|
+
`;
|
|
316
305
|
}
|
|
317
306
|
function hashContent(content) {
|
|
318
307
|
return node_crypto_1.default.createHash('sha1').update(content).digest('hex');
|
|
@@ -329,13 +318,12 @@ async function readManifest(manifestPath) {
|
|
|
329
318
|
async function writeManifest(manifestPath, manifest) {
|
|
330
319
|
await promises_1.default.writeFile(manifestPath, JSON.stringify(manifest, null, 2), 'utf8');
|
|
331
320
|
}
|
|
332
|
-
async function
|
|
333
|
-
const stale = Object.entries(previous).filter(([
|
|
321
|
+
async function removeStaleSelectorModules(previous, next) {
|
|
322
|
+
const stale = Object.entries(previous).filter(([key]) => !next[key]);
|
|
334
323
|
let removed = 0;
|
|
335
324
|
for (const [, entry] of stale) {
|
|
336
|
-
const targetPath = node_path_1.default.join(outDir, entry.file);
|
|
337
325
|
try {
|
|
338
|
-
await promises_1.default.unlink(
|
|
326
|
+
await promises_1.default.unlink(entry.file);
|
|
339
327
|
removed += 1;
|
|
340
328
|
}
|
|
341
329
|
catch {
|
|
@@ -344,25 +332,6 @@ async function removeStaleDeclarations(previous, next, outDir) {
|
|
|
344
332
|
}
|
|
345
333
|
return removed;
|
|
346
334
|
}
|
|
347
|
-
async function writeTypesIndex(indexPath, manifest, outDir) {
|
|
348
|
-
const header = '// Generated by @knighted/css/generate-types\n// Do not edit.\n';
|
|
349
|
-
const references = Object.values(manifest)
|
|
350
|
-
.sort((a, b) => a.file.localeCompare(b.file))
|
|
351
|
-
.map(entry => {
|
|
352
|
-
const rel = node_path_1.default
|
|
353
|
-
.relative(node_path_1.default.dirname(indexPath), node_path_1.default.join(outDir, entry.file))
|
|
354
|
-
.split(node_path_1.default.sep)
|
|
355
|
-
.join('/');
|
|
356
|
-
return `/// <reference path="${rel}" />`;
|
|
357
|
-
});
|
|
358
|
-
const content = references.length > 0
|
|
359
|
-
? `${header}
|
|
360
|
-
${references.join('\n')}
|
|
361
|
-
`
|
|
362
|
-
: `${header}
|
|
363
|
-
`;
|
|
364
|
-
await promises_1.default.writeFile(indexPath, content, 'utf8');
|
|
365
|
-
}
|
|
366
335
|
function formatErrorMessage(error) {
|
|
367
336
|
if (error instanceof Error && typeof error.message === 'string') {
|
|
368
337
|
return error.message;
|
|
@@ -372,6 +341,23 @@ function formatErrorMessage(error) {
|
|
|
372
341
|
function relativeToRoot(filePath, rootDir) {
|
|
373
342
|
return node_path_1.default.relative(rootDir, filePath) || filePath;
|
|
374
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
|
+
}
|
|
375
361
|
async function fileExists(target) {
|
|
376
362
|
try {
|
|
377
363
|
await promises_1.default.access(target);
|
|
@@ -381,6 +367,78 @@ async function fileExists(target) {
|
|
|
381
367
|
return false;
|
|
382
368
|
}
|
|
383
369
|
}
|
|
370
|
+
async function resolveWithTsconfigPaths(specifier, tsconfig) {
|
|
371
|
+
if (!tsconfig) {
|
|
372
|
+
return undefined;
|
|
373
|
+
}
|
|
374
|
+
if (tsconfig.matchPath) {
|
|
375
|
+
const matched = tsconfig.matchPath(specifier);
|
|
376
|
+
if (matched && (await fileExists(matched))) {
|
|
377
|
+
return matched;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
if (tsconfig.absoluteBaseUrl && isNonRelativeSpecifier(specifier)) {
|
|
381
|
+
const candidate = node_path_1.default.join(tsconfig.absoluteBaseUrl, specifier.split('/').join(node_path_1.default.sep));
|
|
382
|
+
if (await fileExists(candidate)) {
|
|
383
|
+
return candidate;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
return undefined;
|
|
387
|
+
}
|
|
388
|
+
function loadTsconfigResolutionContext(rootDir, loader = get_tsconfig_1.getTsconfig) {
|
|
389
|
+
let result;
|
|
390
|
+
try {
|
|
391
|
+
result = loader(rootDir);
|
|
392
|
+
}
|
|
393
|
+
catch {
|
|
394
|
+
return undefined;
|
|
395
|
+
}
|
|
396
|
+
if (!result) {
|
|
397
|
+
return undefined;
|
|
398
|
+
}
|
|
399
|
+
const compilerOptions = result.config.compilerOptions ?? {};
|
|
400
|
+
const configDir = node_path_1.default.dirname(result.path);
|
|
401
|
+
const absoluteBaseUrl = compilerOptions.baseUrl
|
|
402
|
+
? node_path_1.default.resolve(configDir, compilerOptions.baseUrl)
|
|
403
|
+
: undefined;
|
|
404
|
+
const normalizedPaths = normalizeTsconfigPaths(compilerOptions.paths);
|
|
405
|
+
const matchPath = absoluteBaseUrl && normalizedPaths
|
|
406
|
+
? (0, tsconfig_paths_1.createMatchPath)(absoluteBaseUrl, normalizedPaths)
|
|
407
|
+
: undefined;
|
|
408
|
+
if (!absoluteBaseUrl && !matchPath) {
|
|
409
|
+
return undefined;
|
|
410
|
+
}
|
|
411
|
+
return { absoluteBaseUrl, matchPath };
|
|
412
|
+
}
|
|
413
|
+
function normalizeTsconfigPaths(paths) {
|
|
414
|
+
if (!paths) {
|
|
415
|
+
return undefined;
|
|
416
|
+
}
|
|
417
|
+
const normalized = {};
|
|
418
|
+
for (const [pattern, replacements] of Object.entries(paths)) {
|
|
419
|
+
if (!replacements) {
|
|
420
|
+
continue;
|
|
421
|
+
}
|
|
422
|
+
const values = Array.isArray(replacements) ? replacements : [replacements];
|
|
423
|
+
if (values.length === 0) {
|
|
424
|
+
continue;
|
|
425
|
+
}
|
|
426
|
+
normalized[pattern] = values;
|
|
427
|
+
}
|
|
428
|
+
return Object.keys(normalized).length > 0 ? normalized : undefined;
|
|
429
|
+
}
|
|
430
|
+
function isNonRelativeSpecifier(specifier) {
|
|
431
|
+
if (!specifier) {
|
|
432
|
+
return false;
|
|
433
|
+
}
|
|
434
|
+
if (specifier.startsWith('.') || specifier.startsWith('/')) {
|
|
435
|
+
return false;
|
|
436
|
+
}
|
|
437
|
+
if (/^[a-z][\w+.-]*:/i.test(specifier)) {
|
|
438
|
+
return false;
|
|
439
|
+
}
|
|
440
|
+
return true;
|
|
441
|
+
}
|
|
384
442
|
function createProjectPeerResolver(rootDir) {
|
|
385
443
|
const resolver = getProjectRequire(rootDir);
|
|
386
444
|
return async (name) => {
|
|
@@ -423,7 +481,6 @@ async function runGenerateTypesCli(argv = process.argv.slice(2)) {
|
|
|
423
481
|
rootDir: parsed.rootDir,
|
|
424
482
|
include: parsed.include,
|
|
425
483
|
outDir: parsed.outDir,
|
|
426
|
-
typesRoot: parsed.typesRoot,
|
|
427
484
|
stableNamespace: parsed.stableNamespace,
|
|
428
485
|
});
|
|
429
486
|
reportCliResult(result);
|
|
@@ -438,12 +495,11 @@ function parseCliArgs(argv) {
|
|
|
438
495
|
let rootDir = process.cwd();
|
|
439
496
|
const include = [];
|
|
440
497
|
let outDir;
|
|
441
|
-
let typesRoot;
|
|
442
498
|
let stableNamespace;
|
|
443
499
|
for (let i = 0; i < argv.length; i += 1) {
|
|
444
500
|
const arg = argv[i];
|
|
445
501
|
if (arg === '--help' || arg === '-h') {
|
|
446
|
-
return { rootDir, include, outDir,
|
|
502
|
+
return { rootDir, include, outDir, stableNamespace, help: true };
|
|
447
503
|
}
|
|
448
504
|
if (arg === '--root' || arg === '-r') {
|
|
449
505
|
const value = argv[++i];
|
|
@@ -469,14 +525,6 @@ function parseCliArgs(argv) {
|
|
|
469
525
|
outDir = value;
|
|
470
526
|
continue;
|
|
471
527
|
}
|
|
472
|
-
if (arg === '--types-root') {
|
|
473
|
-
const value = argv[++i];
|
|
474
|
-
if (!value) {
|
|
475
|
-
throw new Error('Missing value for --types-root');
|
|
476
|
-
}
|
|
477
|
-
typesRoot = value;
|
|
478
|
-
continue;
|
|
479
|
-
}
|
|
480
528
|
if (arg === '--stable-namespace') {
|
|
481
529
|
const value = argv[++i];
|
|
482
530
|
if (!value) {
|
|
@@ -490,7 +538,7 @@ function parseCliArgs(argv) {
|
|
|
490
538
|
}
|
|
491
539
|
include.push(arg);
|
|
492
540
|
}
|
|
493
|
-
return { rootDir, include, outDir,
|
|
541
|
+
return { rootDir, include, outDir, stableNamespace };
|
|
494
542
|
}
|
|
495
543
|
function printHelp() {
|
|
496
544
|
console.log(`Usage: knighted-css-generate-types [options]
|
|
@@ -498,32 +546,59 @@ function printHelp() {
|
|
|
498
546
|
Options:
|
|
499
547
|
-r, --root <path> Project root directory (default: cwd)
|
|
500
548
|
-i, --include <path> Additional directories/files to scan (repeatable)
|
|
501
|
-
--out-dir <path>
|
|
502
|
-
--types-root <path> Directory for generated @types entrypoint
|
|
549
|
+
--out-dir <path> Directory to store selector module manifest cache
|
|
503
550
|
--stable-namespace <name> Stable namespace prefix for generated selector maps
|
|
504
551
|
-h, --help Show this help message
|
|
505
552
|
`);
|
|
506
553
|
}
|
|
507
554
|
function reportCliResult(result) {
|
|
508
|
-
if (result.
|
|
509
|
-
console.log('[knighted-css]
|
|
555
|
+
if (result.selectorModulesWritten === 0 && result.selectorModulesRemoved === 0) {
|
|
556
|
+
console.log('[knighted-css] Selector modules are up to date.');
|
|
510
557
|
}
|
|
511
558
|
else {
|
|
512
|
-
console.log(`[knighted-css]
|
|
559
|
+
console.log(`[knighted-css] Selector modules updated: wrote ${result.selectorModulesWritten}, removed ${result.selectorModulesRemoved}.`);
|
|
513
560
|
}
|
|
514
|
-
console.log(`[knighted-css]
|
|
561
|
+
console.log(`[knighted-css] Manifest: ${result.manifestPath}`);
|
|
515
562
|
for (const warning of result.warnings) {
|
|
516
563
|
console.warn(`[knighted-css] ${warning}`);
|
|
517
564
|
}
|
|
518
565
|
}
|
|
566
|
+
function setCssWithMetaImplementation(impl) {
|
|
567
|
+
activeCssWithMeta = impl ?? css_js_1.cssWithMeta;
|
|
568
|
+
}
|
|
569
|
+
function setModuleTypeDetector(detector) {
|
|
570
|
+
moduleTypeDetector = detector ?? node_module_type_1.moduleType;
|
|
571
|
+
}
|
|
572
|
+
function setImportMetaUrlProvider(provider) {
|
|
573
|
+
importMetaUrlProvider = provider ?? getImportMetaUrl;
|
|
574
|
+
}
|
|
519
575
|
exports.__generateTypesInternals = {
|
|
520
576
|
stripInlineLoader,
|
|
521
577
|
splitResourceAndQuery,
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
578
|
+
extractSelectorSourceSpecifier,
|
|
579
|
+
findSpecifierImports,
|
|
580
|
+
resolveImportPath,
|
|
581
|
+
resolvePackageRoot,
|
|
582
|
+
relativeToRoot,
|
|
583
|
+
collectCandidateFiles,
|
|
525
584
|
normalizeIncludeOptions,
|
|
585
|
+
normalizeTsconfigPaths,
|
|
586
|
+
setCssWithMetaImplementation,
|
|
587
|
+
setModuleTypeDetector,
|
|
588
|
+
setImportMetaUrlProvider,
|
|
589
|
+
isNonRelativeSpecifier,
|
|
590
|
+
createProjectPeerResolver,
|
|
591
|
+
getProjectRequire,
|
|
592
|
+
loadTsconfigResolutionContext,
|
|
593
|
+
resolveWithTsconfigPaths,
|
|
526
594
|
parseCliArgs,
|
|
527
595
|
printHelp,
|
|
528
596
|
reportCliResult,
|
|
597
|
+
buildSelectorModuleManifestKey,
|
|
598
|
+
buildSelectorModulePath,
|
|
599
|
+
formatSelectorModuleSource,
|
|
600
|
+
ensureSelectorModule,
|
|
601
|
+
removeStaleSelectorModules,
|
|
602
|
+
readManifest,
|
|
603
|
+
writeManifest,
|
|
529
604
|
};
|