@knighted/css 1.0.0-rc.5 → 1.0.0-rc.7
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 +2 -113
- package/dist/cjs/css.d.cts +2 -4
- package/dist/cjs/loader.cjs +25 -39
- package/dist/cjs/loaderInternals.cjs +71 -0
- package/dist/cjs/loaderInternals.d.cts +16 -0
- package/dist/cjs/moduleInfo.cjs +62 -0
- package/dist/cjs/moduleInfo.d.cts +10 -0
- package/dist/cjs/sassInternals.cjs +132 -0
- package/dist/cjs/sassInternals.d.cts +26 -0
- package/dist/css.d.ts +2 -4
- package/dist/css.js +1 -112
- package/dist/loader.js +23 -37
- package/dist/loaderInternals.d.ts +16 -0
- package/dist/loaderInternals.js +63 -0
- package/dist/moduleInfo.d.ts +10 -0
- package/dist/moduleInfo.js +55 -0
- package/dist/sassInternals.d.ts +26 -0
- package/dist/sassInternals.js +121 -0
- package/loader-queries.d.ts +12 -0
- package/package.json +2 -1
package/dist/cjs/css.cjs
CHANGED
|
@@ -9,10 +9,10 @@ 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 node_url_1 = require("node:url");
|
|
13
12
|
const dependency_tree_1 = __importDefault(require("dependency-tree"));
|
|
14
13
|
const lightningcss_1 = require("lightningcss");
|
|
15
14
|
const helpers_js_1 = require("./helpers.cjs");
|
|
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 = {}) {
|
|
18
18
|
const { css: output } = await cssWithMeta(entry, options);
|
|
@@ -129,7 +129,7 @@ async function compileStyleModule(file, { cwd, peerResolver, resolver, }) {
|
|
|
129
129
|
async function compileSass(filePath, indented, { cwd, peerResolver, resolver, }) {
|
|
130
130
|
const sassModule = await optionalPeer('sass', 'Sass', peerResolver);
|
|
131
131
|
const sass = sassModule;
|
|
132
|
-
const importer = createSassImporter({ cwd, resolver });
|
|
132
|
+
const importer = (0, sassInternals_js_1.createSassImporter)({ cwd, resolver });
|
|
133
133
|
const result = await sass.compileAsync(filePath, {
|
|
134
134
|
style: 'expanded',
|
|
135
135
|
loadPaths: buildSassLoadPaths(filePath),
|
|
@@ -154,117 +154,6 @@ function buildSassLoadPaths(filePath) {
|
|
|
154
154
|
loadPaths.add(node_path_1.default.join(cwd, 'node_modules'));
|
|
155
155
|
return Array.from(loadPaths).filter(dir => dir && (0, node_fs_1.existsSync)(dir));
|
|
156
156
|
}
|
|
157
|
-
function createSassImporter({ cwd, resolver }) {
|
|
158
|
-
if (!resolver)
|
|
159
|
-
return undefined;
|
|
160
|
-
const debug = process.env.KNIGHTED_CSS_DEBUG_SASS === '1';
|
|
161
|
-
return {
|
|
162
|
-
async canonicalize(url, context) {
|
|
163
|
-
if (debug) {
|
|
164
|
-
console.error('[knighted-css:sass] canonicalize request:', url);
|
|
165
|
-
if (context?.containingUrl) {
|
|
166
|
-
console.error('[knighted-css:sass] containing url:', context.containingUrl.href);
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
if (shouldNormalizeSpecifier(url)) {
|
|
170
|
-
const resolvedPath = await resolveAliasSpecifier(url, resolver, cwd);
|
|
171
|
-
if (!resolvedPath) {
|
|
172
|
-
if (debug) {
|
|
173
|
-
console.error('[knighted-css:sass] resolver returned no result for', url);
|
|
174
|
-
}
|
|
175
|
-
return null;
|
|
176
|
-
}
|
|
177
|
-
const fileUrl = (0, node_url_1.pathToFileURL)(resolvedPath);
|
|
178
|
-
if (debug) {
|
|
179
|
-
console.error('[knighted-css:sass] canonical url:', fileUrl.href);
|
|
180
|
-
}
|
|
181
|
-
return fileUrl;
|
|
182
|
-
}
|
|
183
|
-
const relativePath = resolveRelativeSpecifier(url, context?.containingUrl);
|
|
184
|
-
if (relativePath) {
|
|
185
|
-
const fileUrl = (0, node_url_1.pathToFileURL)(relativePath);
|
|
186
|
-
if (debug) {
|
|
187
|
-
console.error('[knighted-css:sass] canonical url:', fileUrl.href);
|
|
188
|
-
}
|
|
189
|
-
return fileUrl;
|
|
190
|
-
}
|
|
191
|
-
return null;
|
|
192
|
-
},
|
|
193
|
-
async load(canonicalUrl) {
|
|
194
|
-
if (debug) {
|
|
195
|
-
console.error('[knighted-css:sass] load request:', canonicalUrl.href);
|
|
196
|
-
}
|
|
197
|
-
const filePath = (0, node_url_1.fileURLToPath)(canonicalUrl);
|
|
198
|
-
const contents = await node_fs_1.promises.readFile(filePath, 'utf8');
|
|
199
|
-
return {
|
|
200
|
-
contents,
|
|
201
|
-
syntax: inferSassSyntax(filePath),
|
|
202
|
-
};
|
|
203
|
-
},
|
|
204
|
-
};
|
|
205
|
-
}
|
|
206
|
-
async function resolveAliasSpecifier(specifier, resolver, cwd) {
|
|
207
|
-
const resolved = await resolver(specifier, { cwd });
|
|
208
|
-
if (!resolved) {
|
|
209
|
-
return undefined;
|
|
210
|
-
}
|
|
211
|
-
if (resolved.startsWith('file://')) {
|
|
212
|
-
return ensureSassPath((0, node_url_1.fileURLToPath)(new URL(resolved)));
|
|
213
|
-
}
|
|
214
|
-
const normalized = node_path_1.default.isAbsolute(resolved) ? resolved : node_path_1.default.resolve(cwd, resolved);
|
|
215
|
-
return ensureSassPath(normalized);
|
|
216
|
-
}
|
|
217
|
-
function shouldNormalizeSpecifier(specifier) {
|
|
218
|
-
const schemeMatch = specifier.match(/^([a-z][\w+.-]*):/i);
|
|
219
|
-
if (!schemeMatch) {
|
|
220
|
-
return false;
|
|
221
|
-
}
|
|
222
|
-
const scheme = schemeMatch[1].toLowerCase();
|
|
223
|
-
if (scheme === 'file' ||
|
|
224
|
-
scheme === 'http' ||
|
|
225
|
-
scheme === 'https' ||
|
|
226
|
-
scheme === 'data' ||
|
|
227
|
-
scheme === 'sass') {
|
|
228
|
-
return false;
|
|
229
|
-
}
|
|
230
|
-
return true;
|
|
231
|
-
}
|
|
232
|
-
function inferSassSyntax(filePath) {
|
|
233
|
-
return filePath.endsWith('.sass') ? 'indented' : 'scss';
|
|
234
|
-
}
|
|
235
|
-
function ensureSassPath(filePath) {
|
|
236
|
-
if ((0, node_fs_1.existsSync)(filePath)) {
|
|
237
|
-
return filePath;
|
|
238
|
-
}
|
|
239
|
-
const ext = node_path_1.default.extname(filePath);
|
|
240
|
-
const dir = node_path_1.default.dirname(filePath);
|
|
241
|
-
const base = node_path_1.default.basename(filePath, ext);
|
|
242
|
-
const partialCandidate = node_path_1.default.join(dir, `_${base}${ext}`);
|
|
243
|
-
if (ext && (0, node_fs_1.existsSync)(partialCandidate)) {
|
|
244
|
-
return partialCandidate;
|
|
245
|
-
}
|
|
246
|
-
const indexCandidate = node_path_1.default.join(dir, base, `index${ext}`);
|
|
247
|
-
if (ext && (0, node_fs_1.existsSync)(indexCandidate)) {
|
|
248
|
-
return indexCandidate;
|
|
249
|
-
}
|
|
250
|
-
const partialIndexCandidate = node_path_1.default.join(dir, base, `_index${ext}`);
|
|
251
|
-
if (ext && (0, node_fs_1.existsSync)(partialIndexCandidate)) {
|
|
252
|
-
return partialIndexCandidate;
|
|
253
|
-
}
|
|
254
|
-
return undefined;
|
|
255
|
-
}
|
|
256
|
-
function resolveRelativeSpecifier(specifier, containingUrl) {
|
|
257
|
-
if (!containingUrl || containingUrl.protocol !== 'file:') {
|
|
258
|
-
return undefined;
|
|
259
|
-
}
|
|
260
|
-
if (/^[a-z][\w+.-]*:/i.test(specifier)) {
|
|
261
|
-
return undefined;
|
|
262
|
-
}
|
|
263
|
-
const containingPath = (0, node_url_1.fileURLToPath)(containingUrl);
|
|
264
|
-
const baseDir = node_path_1.default.dirname(containingPath);
|
|
265
|
-
const candidate = node_path_1.default.resolve(baseDir, specifier);
|
|
266
|
-
return ensureSassPath(candidate);
|
|
267
|
-
}
|
|
268
157
|
async function compileLess(filePath, peerResolver) {
|
|
269
158
|
const mod = await optionalPeer('less', 'Less', peerResolver);
|
|
270
159
|
const less = unwrapModuleNamespace(mod);
|
package/dist/cjs/css.d.cts
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import type { Options as DependencyTreeOpts } from 'dependency-tree';
|
|
2
2
|
import { type TransformOptions as LightningTransformOptions } from 'lightningcss';
|
|
3
3
|
import { type SpecificitySelector, type SpecificityStrategy } from './helpers.cjs';
|
|
4
|
+
import type { CssResolver } from './sassInternals.cjs';
|
|
5
|
+
export type { CssResolver } from './sassInternals.cjs';
|
|
4
6
|
export declare const DEFAULT_EXTENSIONS: string[];
|
|
5
7
|
type LightningCssConfig = boolean | Partial<Omit<LightningTransformOptions<never>, 'code'>>;
|
|
6
|
-
export type CssResolver = (specifier: string, ctx: {
|
|
7
|
-
cwd: string;
|
|
8
|
-
}) => string | Promise<string | undefined>;
|
|
9
8
|
type PeerLoader = (name: string) => Promise<unknown>;
|
|
10
9
|
export interface CssOptions {
|
|
11
10
|
extensions?: string[];
|
|
@@ -35,4 +34,3 @@ export interface CssResult {
|
|
|
35
34
|
export declare function css(entry: string, options?: CssOptions): Promise<string>;
|
|
36
35
|
export declare function cssWithMeta(entry: string, options?: CssOptions): Promise<CssResult>;
|
|
37
36
|
export declare function compileVanillaModule(filePath: string, cwd: string, peerResolver?: PeerLoader): Promise<VanillaCompileResult>;
|
|
38
|
-
export {};
|
package/dist/cjs/loader.cjs
CHANGED
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.pitch = void 0;
|
|
4
4
|
const css_js_1 = require("./css.cjs");
|
|
5
|
+
const moduleInfo_js_1 = require("./moduleInfo.cjs");
|
|
6
|
+
const loaderInternals_js_1 = require("./loaderInternals.cjs");
|
|
5
7
|
const DEFAULT_EXPORT_NAME = 'knightedCss';
|
|
6
|
-
const COMBINED_QUERY_FLAG = 'combined';
|
|
7
8
|
const loader = async function loader(source) {
|
|
8
9
|
const { cssOptions, vanillaOptions } = resolveLoaderOptions(this);
|
|
9
10
|
const css = await extractCss(this, cssOptions);
|
|
@@ -47,7 +48,18 @@ const pitch = function pitch() {
|
|
|
47
48
|
}
|
|
48
49
|
const request = buildProxyRequest(this);
|
|
49
50
|
const { cssOptions } = resolveLoaderOptions(this);
|
|
50
|
-
|
|
51
|
+
const skipSyntheticDefault = hasNamedOnlyQueryFlag(this.resourceQuery);
|
|
52
|
+
const defaultSignalPromise = skipSyntheticDefault
|
|
53
|
+
? Promise.resolve('unknown')
|
|
54
|
+
: (0, moduleInfo_js_1.detectModuleDefaultExport)(this.resourcePath);
|
|
55
|
+
return Promise.all([extractCss(this, cssOptions), defaultSignalPromise]).then(([css, defaultSignal]) => {
|
|
56
|
+
const emitDefault = (0, loaderInternals_js_1.shouldEmitCombinedDefault)({
|
|
57
|
+
request,
|
|
58
|
+
skipSyntheticDefault,
|
|
59
|
+
detection: defaultSignal,
|
|
60
|
+
});
|
|
61
|
+
return createCombinedModule(request, css, { emitDefault });
|
|
62
|
+
});
|
|
51
63
|
};
|
|
52
64
|
exports.pitch = pitch;
|
|
53
65
|
loader.pitch = exports.pitch;
|
|
@@ -87,10 +99,16 @@ function hasCombinedQuery(query) {
|
|
|
87
99
|
return trimmed
|
|
88
100
|
.split('&')
|
|
89
101
|
.filter(Boolean)
|
|
90
|
-
.some(part => isQueryFlag(part, COMBINED_QUERY_FLAG));
|
|
102
|
+
.some(part => (0, loaderInternals_js_1.isQueryFlag)(part, loaderInternals_js_1.COMBINED_QUERY_FLAG));
|
|
103
|
+
}
|
|
104
|
+
function hasNamedOnlyQueryFlag(query) {
|
|
105
|
+
if (!query)
|
|
106
|
+
return false;
|
|
107
|
+
const entries = (0, loaderInternals_js_1.splitQuery)(query);
|
|
108
|
+
return entries.some(part => loaderInternals_js_1.NAMED_ONLY_QUERY_FLAGS.some(flag => (0, loaderInternals_js_1.isQueryFlag)(part, flag)));
|
|
91
109
|
}
|
|
92
110
|
function buildProxyRequest(ctx) {
|
|
93
|
-
const sanitizedQuery = buildSanitizedQuery(ctx.resourceQuery);
|
|
111
|
+
const sanitizedQuery = (0, loaderInternals_js_1.buildSanitizedQuery)(ctx.resourceQuery);
|
|
94
112
|
const rawRequest = getRawRequest(ctx);
|
|
95
113
|
if (rawRequest) {
|
|
96
114
|
const stripped = stripResourceQuery(rawRequest);
|
|
@@ -115,36 +133,14 @@ function stripResourceQuery(request) {
|
|
|
115
133
|
const idx = request.indexOf('?');
|
|
116
134
|
return idx >= 0 ? request.slice(0, idx) : request;
|
|
117
135
|
}
|
|
118
|
-
function
|
|
119
|
-
|
|
120
|
-
return '';
|
|
121
|
-
const entries = splitQuery(query).filter(part => {
|
|
122
|
-
return !isQueryFlag(part, COMBINED_QUERY_FLAG) && !isQueryFlag(part, 'knighted-css');
|
|
123
|
-
});
|
|
124
|
-
return entries.length > 0 ? `?${entries.join('&')}` : '';
|
|
125
|
-
}
|
|
126
|
-
function splitQuery(query) {
|
|
127
|
-
const trimmed = query.startsWith('?') ? query.slice(1) : query;
|
|
128
|
-
if (!trimmed)
|
|
129
|
-
return [];
|
|
130
|
-
return trimmed.split('&').filter(Boolean);
|
|
131
|
-
}
|
|
132
|
-
function isQueryFlag(entry, flag) {
|
|
133
|
-
const [rawKey] = entry.split('=');
|
|
134
|
-
try {
|
|
135
|
-
return decodeURIComponent(rawKey) === flag;
|
|
136
|
-
}
|
|
137
|
-
catch {
|
|
138
|
-
return rawKey === flag;
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
function createCombinedModule(request, css) {
|
|
136
|
+
function createCombinedModule(request, css, options) {
|
|
137
|
+
const shouldEmitDefault = options?.emitDefault ?? (0, loaderInternals_js_1.shouldForwardDefaultExport)(request);
|
|
142
138
|
const requestLiteral = JSON.stringify(request);
|
|
143
139
|
const lines = [
|
|
144
140
|
`import * as __knightedModule from ${requestLiteral};`,
|
|
145
141
|
`export * from ${requestLiteral};`,
|
|
146
142
|
];
|
|
147
|
-
if (
|
|
143
|
+
if (shouldEmitDefault) {
|
|
148
144
|
lines.push(`const __knightedDefault =
|
|
149
145
|
typeof __knightedModule.default !== 'undefined'
|
|
150
146
|
? __knightedModule.default
|
|
@@ -153,13 +149,3 @@ typeof __knightedModule.default !== 'undefined'
|
|
|
153
149
|
lines.push(buildInjection(css));
|
|
154
150
|
return lines.join('\n');
|
|
155
151
|
}
|
|
156
|
-
function shouldForwardDefaultExport(request) {
|
|
157
|
-
const [pathPart] = request.split('?');
|
|
158
|
-
if (!pathPart)
|
|
159
|
-
return true;
|
|
160
|
-
const lower = pathPart.toLowerCase();
|
|
161
|
-
if (lower.endsWith('.css.ts') || lower.endsWith('.css.js')) {
|
|
162
|
-
return false;
|
|
163
|
-
}
|
|
164
|
-
return true;
|
|
165
|
-
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.__loaderInternals = exports.NAMED_ONLY_QUERY_FLAGS = exports.COMBINED_QUERY_FLAG = void 0;
|
|
4
|
+
exports.splitQuery = splitQuery;
|
|
5
|
+
exports.isQueryFlag = isQueryFlag;
|
|
6
|
+
exports.buildSanitizedQuery = buildSanitizedQuery;
|
|
7
|
+
exports.shouldForwardDefaultExport = shouldForwardDefaultExport;
|
|
8
|
+
exports.shouldEmitCombinedDefault = shouldEmitCombinedDefault;
|
|
9
|
+
exports.COMBINED_QUERY_FLAG = 'combined';
|
|
10
|
+
exports.NAMED_ONLY_QUERY_FLAGS = ['named-only', 'no-default'];
|
|
11
|
+
function splitQuery(query) {
|
|
12
|
+
const trimmed = query.startsWith('?') ? query.slice(1) : query;
|
|
13
|
+
if (!trimmed)
|
|
14
|
+
return [];
|
|
15
|
+
return trimmed.split('&').filter(Boolean);
|
|
16
|
+
}
|
|
17
|
+
function isQueryFlag(entry, flag) {
|
|
18
|
+
const [rawKey] = entry.split('=');
|
|
19
|
+
try {
|
|
20
|
+
return decodeURIComponent(rawKey) === flag;
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return rawKey === flag;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function buildSanitizedQuery(query) {
|
|
27
|
+
if (!query)
|
|
28
|
+
return '';
|
|
29
|
+
const entries = splitQuery(query).filter(part => {
|
|
30
|
+
if (isQueryFlag(part, exports.COMBINED_QUERY_FLAG)) {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
if (isQueryFlag(part, 'knighted-css')) {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
if (exports.NAMED_ONLY_QUERY_FLAGS.some(flag => isQueryFlag(part, flag))) {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
return true;
|
|
40
|
+
});
|
|
41
|
+
return entries.length > 0 ? `?${entries.join('&')}` : '';
|
|
42
|
+
}
|
|
43
|
+
function shouldForwardDefaultExport(request) {
|
|
44
|
+
const [pathPart] = request.split('?');
|
|
45
|
+
if (!pathPart)
|
|
46
|
+
return true;
|
|
47
|
+
const lower = pathPart.toLowerCase();
|
|
48
|
+
if (lower.endsWith('.css.ts') || lower.endsWith('.css.js')) {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
function shouldEmitCombinedDefault(options) {
|
|
54
|
+
if (options.skipSyntheticDefault) {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
if (!shouldForwardDefaultExport(options.request)) {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
if (options.detection === 'has-default') {
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
if (options.detection === 'no-default') {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
exports.__loaderInternals = {
|
|
69
|
+
buildSanitizedQuery,
|
|
70
|
+
shouldEmitCombinedDefault,
|
|
71
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ModuleDefaultSignal } from './moduleInfo.cjs';
|
|
2
|
+
export declare const COMBINED_QUERY_FLAG = "combined";
|
|
3
|
+
export declare const NAMED_ONLY_QUERY_FLAGS: readonly ["named-only", "no-default"];
|
|
4
|
+
export declare function splitQuery(query: string): string[];
|
|
5
|
+
export declare function isQueryFlag(entry: string, flag: string): boolean;
|
|
6
|
+
export declare function buildSanitizedQuery(query?: string | null): string;
|
|
7
|
+
export declare function shouldForwardDefaultExport(request: string): boolean;
|
|
8
|
+
export declare function shouldEmitCombinedDefault(options: {
|
|
9
|
+
detection: ModuleDefaultSignal;
|
|
10
|
+
request: string;
|
|
11
|
+
skipSyntheticDefault: boolean;
|
|
12
|
+
}): boolean;
|
|
13
|
+
export declare const __loaderInternals: {
|
|
14
|
+
buildSanitizedQuery: typeof buildSanitizedQuery;
|
|
15
|
+
shouldEmitCombinedDefault: typeof shouldEmitCombinedDefault;
|
|
16
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.__moduleInfoInternals = void 0;
|
|
7
|
+
exports.detectModuleDefaultExport = detectModuleDefaultExport;
|
|
8
|
+
const promises_1 = require("node:fs/promises");
|
|
9
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
10
|
+
const es_module_lexer_1 = require("es-module-lexer");
|
|
11
|
+
const DETECTABLE_EXTENSIONS = new Set([
|
|
12
|
+
'.js',
|
|
13
|
+
'.jsx',
|
|
14
|
+
'.ts',
|
|
15
|
+
'.tsx',
|
|
16
|
+
'.mjs',
|
|
17
|
+
'.mts',
|
|
18
|
+
'.cjs',
|
|
19
|
+
'.cts',
|
|
20
|
+
]);
|
|
21
|
+
let lexerInit;
|
|
22
|
+
let lexerOverrides;
|
|
23
|
+
function ensureLexerInitialized() {
|
|
24
|
+
if (!lexerInit) {
|
|
25
|
+
lexerInit = es_module_lexer_1.init;
|
|
26
|
+
}
|
|
27
|
+
return lexerInit;
|
|
28
|
+
}
|
|
29
|
+
async function detectModuleDefaultExport(filePath) {
|
|
30
|
+
if (!DETECTABLE_EXTENSIONS.has(node_path_1.default.extname(filePath))) {
|
|
31
|
+
return 'unknown';
|
|
32
|
+
}
|
|
33
|
+
let source;
|
|
34
|
+
try {
|
|
35
|
+
source = await (0, promises_1.readFile)(filePath, 'utf8');
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return 'unknown';
|
|
39
|
+
}
|
|
40
|
+
try {
|
|
41
|
+
await ensureLexerInitialized();
|
|
42
|
+
const [, exports] = (lexerOverrides?.parse ?? es_module_lexer_1.parse)(source, filePath);
|
|
43
|
+
if (exports.some(entry => entry.n === 'default')) {
|
|
44
|
+
return 'has-default';
|
|
45
|
+
}
|
|
46
|
+
if (exports.length === 0) {
|
|
47
|
+
return 'unknown';
|
|
48
|
+
}
|
|
49
|
+
return 'no-default';
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return 'unknown';
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
exports.__moduleInfoInternals = {
|
|
56
|
+
setLexerOverrides(overrides) {
|
|
57
|
+
lexerOverrides = overrides;
|
|
58
|
+
if (!overrides) {
|
|
59
|
+
lexerInit = undefined;
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { parse } from 'es-module-lexer';
|
|
2
|
+
export type ModuleDefaultSignal = 'has-default' | 'no-default' | 'unknown';
|
|
3
|
+
type LexerOverrides = {
|
|
4
|
+
parse?: typeof parse;
|
|
5
|
+
};
|
|
6
|
+
export declare function detectModuleDefaultExport(filePath: string): Promise<ModuleDefaultSignal>;
|
|
7
|
+
export declare const __moduleInfoInternals: {
|
|
8
|
+
setLexerOverrides(overrides?: LexerOverrides): void;
|
|
9
|
+
};
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.__sassInternals = void 0;
|
|
7
|
+
exports.createSassImporter = createSassImporter;
|
|
8
|
+
exports.resolveAliasSpecifier = resolveAliasSpecifier;
|
|
9
|
+
exports.shouldNormalizeSpecifier = shouldNormalizeSpecifier;
|
|
10
|
+
exports.ensureSassPath = ensureSassPath;
|
|
11
|
+
exports.resolveRelativeSpecifier = resolveRelativeSpecifier;
|
|
12
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
13
|
+
const node_fs_1 = require("node:fs");
|
|
14
|
+
const node_url_1 = require("node:url");
|
|
15
|
+
function createSassImporter({ cwd, resolver, }) {
|
|
16
|
+
if (!resolver)
|
|
17
|
+
return undefined;
|
|
18
|
+
const debug = process.env.KNIGHTED_CSS_DEBUG_SASS === '1';
|
|
19
|
+
return {
|
|
20
|
+
async canonicalize(url, context) {
|
|
21
|
+
if (debug) {
|
|
22
|
+
console.error('[knighted-css:sass] canonicalize request:', url);
|
|
23
|
+
if (context?.containingUrl) {
|
|
24
|
+
console.error('[knighted-css:sass] containing url:', context.containingUrl.href);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
if (shouldNormalizeSpecifier(url)) {
|
|
28
|
+
const resolvedPath = await resolveAliasSpecifier(url, resolver, cwd);
|
|
29
|
+
if (!resolvedPath) {
|
|
30
|
+
if (debug) {
|
|
31
|
+
console.error('[knighted-css:sass] resolver returned no result for', url);
|
|
32
|
+
}
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
const fileUrl = (0, node_url_1.pathToFileURL)(resolvedPath);
|
|
36
|
+
if (debug) {
|
|
37
|
+
console.error('[knighted-css:sass] canonical url:', fileUrl.href);
|
|
38
|
+
}
|
|
39
|
+
return fileUrl;
|
|
40
|
+
}
|
|
41
|
+
const relativePath = resolveRelativeSpecifier(url, context?.containingUrl);
|
|
42
|
+
if (relativePath) {
|
|
43
|
+
const fileUrl = (0, node_url_1.pathToFileURL)(relativePath);
|
|
44
|
+
if (debug) {
|
|
45
|
+
console.error('[knighted-css:sass] canonical url:', fileUrl.href);
|
|
46
|
+
}
|
|
47
|
+
return fileUrl;
|
|
48
|
+
}
|
|
49
|
+
return null;
|
|
50
|
+
},
|
|
51
|
+
async load(canonicalUrl) {
|
|
52
|
+
if (debug) {
|
|
53
|
+
console.error('[knighted-css:sass] load request:', canonicalUrl.href);
|
|
54
|
+
}
|
|
55
|
+
const filePath = (0, node_url_1.fileURLToPath)(canonicalUrl);
|
|
56
|
+
const contents = await node_fs_1.promises.readFile(filePath, 'utf8');
|
|
57
|
+
return {
|
|
58
|
+
contents,
|
|
59
|
+
syntax: inferSassSyntax(filePath),
|
|
60
|
+
};
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
async function resolveAliasSpecifier(specifier, resolver, cwd) {
|
|
65
|
+
const resolved = await resolver(specifier, { cwd });
|
|
66
|
+
if (!resolved) {
|
|
67
|
+
return undefined;
|
|
68
|
+
}
|
|
69
|
+
if (resolved.startsWith('file://')) {
|
|
70
|
+
return ensureSassPath((0, node_url_1.fileURLToPath)(new URL(resolved)));
|
|
71
|
+
}
|
|
72
|
+
const normalized = node_path_1.default.isAbsolute(resolved) ? resolved : node_path_1.default.resolve(cwd, resolved);
|
|
73
|
+
return ensureSassPath(normalized);
|
|
74
|
+
}
|
|
75
|
+
function shouldNormalizeSpecifier(specifier) {
|
|
76
|
+
const schemeMatch = specifier.match(/^([a-z][\w+.-]*):/i);
|
|
77
|
+
if (!schemeMatch) {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
const scheme = schemeMatch[1].toLowerCase();
|
|
81
|
+
if (scheme === 'file' ||
|
|
82
|
+
scheme === 'http' ||
|
|
83
|
+
scheme === 'https' ||
|
|
84
|
+
scheme === 'data' ||
|
|
85
|
+
scheme === 'sass') {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
function inferSassSyntax(filePath) {
|
|
91
|
+
return filePath.endsWith('.sass') ? 'indented' : 'scss';
|
|
92
|
+
}
|
|
93
|
+
function ensureSassPath(filePath) {
|
|
94
|
+
if ((0, node_fs_1.existsSync)(filePath)) {
|
|
95
|
+
return filePath;
|
|
96
|
+
}
|
|
97
|
+
const ext = node_path_1.default.extname(filePath);
|
|
98
|
+
const dir = node_path_1.default.dirname(filePath);
|
|
99
|
+
const base = node_path_1.default.basename(filePath, ext);
|
|
100
|
+
const partialCandidate = node_path_1.default.join(dir, `_${base}${ext}`);
|
|
101
|
+
if (ext && (0, node_fs_1.existsSync)(partialCandidate)) {
|
|
102
|
+
return partialCandidate;
|
|
103
|
+
}
|
|
104
|
+
const indexCandidate = node_path_1.default.join(dir, base, `index${ext}`);
|
|
105
|
+
if (ext && (0, node_fs_1.existsSync)(indexCandidate)) {
|
|
106
|
+
return indexCandidate;
|
|
107
|
+
}
|
|
108
|
+
const partialIndexCandidate = node_path_1.default.join(dir, base, `_index${ext}`);
|
|
109
|
+
if (ext && (0, node_fs_1.existsSync)(partialIndexCandidate)) {
|
|
110
|
+
return partialIndexCandidate;
|
|
111
|
+
}
|
|
112
|
+
return undefined;
|
|
113
|
+
}
|
|
114
|
+
function resolveRelativeSpecifier(specifier, containingUrl) {
|
|
115
|
+
if (!containingUrl || containingUrl.protocol !== 'file:') {
|
|
116
|
+
return undefined;
|
|
117
|
+
}
|
|
118
|
+
if (/^[a-z][\w+.-]*:/i.test(specifier)) {
|
|
119
|
+
return undefined;
|
|
120
|
+
}
|
|
121
|
+
const containingPath = (0, node_url_1.fileURLToPath)(containingUrl);
|
|
122
|
+
const baseDir = node_path_1.default.dirname(containingPath);
|
|
123
|
+
const candidate = node_path_1.default.resolve(baseDir, specifier);
|
|
124
|
+
return ensureSassPath(candidate);
|
|
125
|
+
}
|
|
126
|
+
exports.__sassInternals = {
|
|
127
|
+
createSassImporter,
|
|
128
|
+
resolveAliasSpecifier,
|
|
129
|
+
shouldNormalizeSpecifier,
|
|
130
|
+
ensureSassPath,
|
|
131
|
+
resolveRelativeSpecifier,
|
|
132
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export type CssResolver = (specifier: string, ctx: {
|
|
2
|
+
cwd: string;
|
|
3
|
+
}) => string | Promise<string | undefined>;
|
|
4
|
+
export declare function createSassImporter({ cwd, resolver, }: {
|
|
5
|
+
cwd: string;
|
|
6
|
+
resolver?: CssResolver;
|
|
7
|
+
}): {
|
|
8
|
+
canonicalize(url: string, context?: {
|
|
9
|
+
containingUrl?: URL | null;
|
|
10
|
+
}): Promise<import("node:url").URL | null>;
|
|
11
|
+
load(canonicalUrl: URL): Promise<{
|
|
12
|
+
contents: string;
|
|
13
|
+
syntax: "scss" | "indented";
|
|
14
|
+
}>;
|
|
15
|
+
} | undefined;
|
|
16
|
+
export declare function resolveAliasSpecifier(specifier: string, resolver: CssResolver, cwd: string): Promise<string | undefined>;
|
|
17
|
+
export declare function shouldNormalizeSpecifier(specifier: string): boolean;
|
|
18
|
+
export declare function ensureSassPath(filePath: string): string | undefined;
|
|
19
|
+
export declare function resolveRelativeSpecifier(specifier: string, containingUrl?: URL | null): string | undefined;
|
|
20
|
+
export declare const __sassInternals: {
|
|
21
|
+
createSassImporter: typeof createSassImporter;
|
|
22
|
+
resolveAliasSpecifier: typeof resolveAliasSpecifier;
|
|
23
|
+
shouldNormalizeSpecifier: typeof shouldNormalizeSpecifier;
|
|
24
|
+
ensureSassPath: typeof ensureSassPath;
|
|
25
|
+
resolveRelativeSpecifier: typeof resolveRelativeSpecifier;
|
|
26
|
+
};
|
package/dist/css.d.ts
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import type { Options as DependencyTreeOpts } from 'dependency-tree';
|
|
2
2
|
import { type TransformOptions as LightningTransformOptions } from 'lightningcss';
|
|
3
3
|
import { type SpecificitySelector, type SpecificityStrategy } from './helpers.js';
|
|
4
|
+
import type { CssResolver } from './sassInternals.js';
|
|
5
|
+
export type { CssResolver } from './sassInternals.js';
|
|
4
6
|
export declare const DEFAULT_EXTENSIONS: string[];
|
|
5
7
|
type LightningCssConfig = boolean | Partial<Omit<LightningTransformOptions<never>, 'code'>>;
|
|
6
|
-
export type CssResolver = (specifier: string, ctx: {
|
|
7
|
-
cwd: string;
|
|
8
|
-
}) => string | Promise<string | undefined>;
|
|
9
8
|
type PeerLoader = (name: string) => Promise<unknown>;
|
|
10
9
|
export interface CssOptions {
|
|
11
10
|
extensions?: string[];
|
|
@@ -35,4 +34,3 @@ export interface CssResult {
|
|
|
35
34
|
export declare function css(entry: string, options?: CssOptions): Promise<string>;
|
|
36
35
|
export declare function cssWithMeta(entry: string, options?: CssOptions): Promise<CssResult>;
|
|
37
36
|
export declare function compileVanillaModule(filePath: string, cwd: string, peerResolver?: PeerLoader): Promise<VanillaCompileResult>;
|
|
38
|
-
export {};
|
package/dist/css.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import { existsSync, promises as fs } from 'node:fs';
|
|
3
|
-
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
4
3
|
import dependencyTree from 'dependency-tree';
|
|
5
4
|
import { composeVisitors, transform as lightningTransform, } from 'lightningcss';
|
|
6
5
|
import { applyStringSpecificityBoost, buildSpecificityVisitor, } from './helpers.js';
|
|
6
|
+
import { createSassImporter } from './sassInternals.js';
|
|
7
7
|
export const DEFAULT_EXTENSIONS = ['.css', '.scss', '.sass', '.less', '.css.ts'];
|
|
8
8
|
export async function css(entry, options = {}) {
|
|
9
9
|
const { css: output } = await cssWithMeta(entry, options);
|
|
@@ -145,117 +145,6 @@ function buildSassLoadPaths(filePath) {
|
|
|
145
145
|
loadPaths.add(path.join(cwd, 'node_modules'));
|
|
146
146
|
return Array.from(loadPaths).filter(dir => dir && existsSync(dir));
|
|
147
147
|
}
|
|
148
|
-
function createSassImporter({ cwd, resolver }) {
|
|
149
|
-
if (!resolver)
|
|
150
|
-
return undefined;
|
|
151
|
-
const debug = process.env.KNIGHTED_CSS_DEBUG_SASS === '1';
|
|
152
|
-
return {
|
|
153
|
-
async canonicalize(url, context) {
|
|
154
|
-
if (debug) {
|
|
155
|
-
console.error('[knighted-css:sass] canonicalize request:', url);
|
|
156
|
-
if (context?.containingUrl) {
|
|
157
|
-
console.error('[knighted-css:sass] containing url:', context.containingUrl.href);
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
if (shouldNormalizeSpecifier(url)) {
|
|
161
|
-
const resolvedPath = await resolveAliasSpecifier(url, resolver, cwd);
|
|
162
|
-
if (!resolvedPath) {
|
|
163
|
-
if (debug) {
|
|
164
|
-
console.error('[knighted-css:sass] resolver returned no result for', url);
|
|
165
|
-
}
|
|
166
|
-
return null;
|
|
167
|
-
}
|
|
168
|
-
const fileUrl = pathToFileURL(resolvedPath);
|
|
169
|
-
if (debug) {
|
|
170
|
-
console.error('[knighted-css:sass] canonical url:', fileUrl.href);
|
|
171
|
-
}
|
|
172
|
-
return fileUrl;
|
|
173
|
-
}
|
|
174
|
-
const relativePath = resolveRelativeSpecifier(url, context?.containingUrl);
|
|
175
|
-
if (relativePath) {
|
|
176
|
-
const fileUrl = pathToFileURL(relativePath);
|
|
177
|
-
if (debug) {
|
|
178
|
-
console.error('[knighted-css:sass] canonical url:', fileUrl.href);
|
|
179
|
-
}
|
|
180
|
-
return fileUrl;
|
|
181
|
-
}
|
|
182
|
-
return null;
|
|
183
|
-
},
|
|
184
|
-
async load(canonicalUrl) {
|
|
185
|
-
if (debug) {
|
|
186
|
-
console.error('[knighted-css:sass] load request:', canonicalUrl.href);
|
|
187
|
-
}
|
|
188
|
-
const filePath = fileURLToPath(canonicalUrl);
|
|
189
|
-
const contents = await fs.readFile(filePath, 'utf8');
|
|
190
|
-
return {
|
|
191
|
-
contents,
|
|
192
|
-
syntax: inferSassSyntax(filePath),
|
|
193
|
-
};
|
|
194
|
-
},
|
|
195
|
-
};
|
|
196
|
-
}
|
|
197
|
-
async function resolveAliasSpecifier(specifier, resolver, cwd) {
|
|
198
|
-
const resolved = await resolver(specifier, { cwd });
|
|
199
|
-
if (!resolved) {
|
|
200
|
-
return undefined;
|
|
201
|
-
}
|
|
202
|
-
if (resolved.startsWith('file://')) {
|
|
203
|
-
return ensureSassPath(fileURLToPath(new URL(resolved)));
|
|
204
|
-
}
|
|
205
|
-
const normalized = path.isAbsolute(resolved) ? resolved : path.resolve(cwd, resolved);
|
|
206
|
-
return ensureSassPath(normalized);
|
|
207
|
-
}
|
|
208
|
-
function shouldNormalizeSpecifier(specifier) {
|
|
209
|
-
const schemeMatch = specifier.match(/^([a-z][\w+.-]*):/i);
|
|
210
|
-
if (!schemeMatch) {
|
|
211
|
-
return false;
|
|
212
|
-
}
|
|
213
|
-
const scheme = schemeMatch[1].toLowerCase();
|
|
214
|
-
if (scheme === 'file' ||
|
|
215
|
-
scheme === 'http' ||
|
|
216
|
-
scheme === 'https' ||
|
|
217
|
-
scheme === 'data' ||
|
|
218
|
-
scheme === 'sass') {
|
|
219
|
-
return false;
|
|
220
|
-
}
|
|
221
|
-
return true;
|
|
222
|
-
}
|
|
223
|
-
function inferSassSyntax(filePath) {
|
|
224
|
-
return filePath.endsWith('.sass') ? 'indented' : 'scss';
|
|
225
|
-
}
|
|
226
|
-
function ensureSassPath(filePath) {
|
|
227
|
-
if (existsSync(filePath)) {
|
|
228
|
-
return filePath;
|
|
229
|
-
}
|
|
230
|
-
const ext = path.extname(filePath);
|
|
231
|
-
const dir = path.dirname(filePath);
|
|
232
|
-
const base = path.basename(filePath, ext);
|
|
233
|
-
const partialCandidate = path.join(dir, `_${base}${ext}`);
|
|
234
|
-
if (ext && existsSync(partialCandidate)) {
|
|
235
|
-
return partialCandidate;
|
|
236
|
-
}
|
|
237
|
-
const indexCandidate = path.join(dir, base, `index${ext}`);
|
|
238
|
-
if (ext && existsSync(indexCandidate)) {
|
|
239
|
-
return indexCandidate;
|
|
240
|
-
}
|
|
241
|
-
const partialIndexCandidate = path.join(dir, base, `_index${ext}`);
|
|
242
|
-
if (ext && existsSync(partialIndexCandidate)) {
|
|
243
|
-
return partialIndexCandidate;
|
|
244
|
-
}
|
|
245
|
-
return undefined;
|
|
246
|
-
}
|
|
247
|
-
function resolveRelativeSpecifier(specifier, containingUrl) {
|
|
248
|
-
if (!containingUrl || containingUrl.protocol !== 'file:') {
|
|
249
|
-
return undefined;
|
|
250
|
-
}
|
|
251
|
-
if (/^[a-z][\w+.-]*:/i.test(specifier)) {
|
|
252
|
-
return undefined;
|
|
253
|
-
}
|
|
254
|
-
const containingPath = fileURLToPath(containingUrl);
|
|
255
|
-
const baseDir = path.dirname(containingPath);
|
|
256
|
-
const candidate = path.resolve(baseDir, specifier);
|
|
257
|
-
return ensureSassPath(candidate);
|
|
258
|
-
}
|
|
259
148
|
async function compileLess(filePath, peerResolver) {
|
|
260
149
|
const mod = await optionalPeer('less', 'Less', peerResolver);
|
|
261
150
|
const less = unwrapModuleNamespace(mod);
|
package/dist/loader.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { cssWithMeta, compileVanillaModule } from './css.js';
|
|
2
|
+
import { detectModuleDefaultExport } from './moduleInfo.js';
|
|
3
|
+
import { buildSanitizedQuery, COMBINED_QUERY_FLAG, isQueryFlag, NAMED_ONLY_QUERY_FLAGS, shouldEmitCombinedDefault, shouldForwardDefaultExport, splitQuery, } from './loaderInternals.js';
|
|
2
4
|
const DEFAULT_EXPORT_NAME = 'knightedCss';
|
|
3
|
-
const COMBINED_QUERY_FLAG = 'combined';
|
|
4
5
|
const loader = async function loader(source) {
|
|
5
6
|
const { cssOptions, vanillaOptions } = resolveLoaderOptions(this);
|
|
6
7
|
const css = await extractCss(this, cssOptions);
|
|
@@ -44,7 +45,18 @@ export const pitch = function pitch() {
|
|
|
44
45
|
}
|
|
45
46
|
const request = buildProxyRequest(this);
|
|
46
47
|
const { cssOptions } = resolveLoaderOptions(this);
|
|
47
|
-
|
|
48
|
+
const skipSyntheticDefault = hasNamedOnlyQueryFlag(this.resourceQuery);
|
|
49
|
+
const defaultSignalPromise = skipSyntheticDefault
|
|
50
|
+
? Promise.resolve('unknown')
|
|
51
|
+
: detectModuleDefaultExport(this.resourcePath);
|
|
52
|
+
return Promise.all([extractCss(this, cssOptions), defaultSignalPromise]).then(([css, defaultSignal]) => {
|
|
53
|
+
const emitDefault = shouldEmitCombinedDefault({
|
|
54
|
+
request,
|
|
55
|
+
skipSyntheticDefault,
|
|
56
|
+
detection: defaultSignal,
|
|
57
|
+
});
|
|
58
|
+
return createCombinedModule(request, css, { emitDefault });
|
|
59
|
+
});
|
|
48
60
|
};
|
|
49
61
|
loader.pitch = pitch;
|
|
50
62
|
export default loader;
|
|
@@ -85,6 +97,12 @@ function hasCombinedQuery(query) {
|
|
|
85
97
|
.filter(Boolean)
|
|
86
98
|
.some(part => isQueryFlag(part, COMBINED_QUERY_FLAG));
|
|
87
99
|
}
|
|
100
|
+
function hasNamedOnlyQueryFlag(query) {
|
|
101
|
+
if (!query)
|
|
102
|
+
return false;
|
|
103
|
+
const entries = splitQuery(query);
|
|
104
|
+
return entries.some(part => NAMED_ONLY_QUERY_FLAGS.some(flag => isQueryFlag(part, flag)));
|
|
105
|
+
}
|
|
88
106
|
function buildProxyRequest(ctx) {
|
|
89
107
|
const sanitizedQuery = buildSanitizedQuery(ctx.resourceQuery);
|
|
90
108
|
const rawRequest = getRawRequest(ctx);
|
|
@@ -111,36 +129,14 @@ function stripResourceQuery(request) {
|
|
|
111
129
|
const idx = request.indexOf('?');
|
|
112
130
|
return idx >= 0 ? request.slice(0, idx) : request;
|
|
113
131
|
}
|
|
114
|
-
function
|
|
115
|
-
|
|
116
|
-
return '';
|
|
117
|
-
const entries = splitQuery(query).filter(part => {
|
|
118
|
-
return !isQueryFlag(part, COMBINED_QUERY_FLAG) && !isQueryFlag(part, 'knighted-css');
|
|
119
|
-
});
|
|
120
|
-
return entries.length > 0 ? `?${entries.join('&')}` : '';
|
|
121
|
-
}
|
|
122
|
-
function splitQuery(query) {
|
|
123
|
-
const trimmed = query.startsWith('?') ? query.slice(1) : query;
|
|
124
|
-
if (!trimmed)
|
|
125
|
-
return [];
|
|
126
|
-
return trimmed.split('&').filter(Boolean);
|
|
127
|
-
}
|
|
128
|
-
function isQueryFlag(entry, flag) {
|
|
129
|
-
const [rawKey] = entry.split('=');
|
|
130
|
-
try {
|
|
131
|
-
return decodeURIComponent(rawKey) === flag;
|
|
132
|
-
}
|
|
133
|
-
catch {
|
|
134
|
-
return rawKey === flag;
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
function createCombinedModule(request, css) {
|
|
132
|
+
function createCombinedModule(request, css, options) {
|
|
133
|
+
const shouldEmitDefault = options?.emitDefault ?? shouldForwardDefaultExport(request);
|
|
138
134
|
const requestLiteral = JSON.stringify(request);
|
|
139
135
|
const lines = [
|
|
140
136
|
`import * as __knightedModule from ${requestLiteral};`,
|
|
141
137
|
`export * from ${requestLiteral};`,
|
|
142
138
|
];
|
|
143
|
-
if (
|
|
139
|
+
if (shouldEmitDefault) {
|
|
144
140
|
lines.push(`const __knightedDefault =
|
|
145
141
|
typeof __knightedModule.default !== 'undefined'
|
|
146
142
|
? __knightedModule.default
|
|
@@ -149,13 +145,3 @@ typeof __knightedModule.default !== 'undefined'
|
|
|
149
145
|
lines.push(buildInjection(css));
|
|
150
146
|
return lines.join('\n');
|
|
151
147
|
}
|
|
152
|
-
function shouldForwardDefaultExport(request) {
|
|
153
|
-
const [pathPart] = request.split('?');
|
|
154
|
-
if (!pathPart)
|
|
155
|
-
return true;
|
|
156
|
-
const lower = pathPart.toLowerCase();
|
|
157
|
-
if (lower.endsWith('.css.ts') || lower.endsWith('.css.js')) {
|
|
158
|
-
return false;
|
|
159
|
-
}
|
|
160
|
-
return true;
|
|
161
|
-
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ModuleDefaultSignal } from './moduleInfo.js';
|
|
2
|
+
export declare const COMBINED_QUERY_FLAG = "combined";
|
|
3
|
+
export declare const NAMED_ONLY_QUERY_FLAGS: readonly ["named-only", "no-default"];
|
|
4
|
+
export declare function splitQuery(query: string): string[];
|
|
5
|
+
export declare function isQueryFlag(entry: string, flag: string): boolean;
|
|
6
|
+
export declare function buildSanitizedQuery(query?: string | null): string;
|
|
7
|
+
export declare function shouldForwardDefaultExport(request: string): boolean;
|
|
8
|
+
export declare function shouldEmitCombinedDefault(options: {
|
|
9
|
+
detection: ModuleDefaultSignal;
|
|
10
|
+
request: string;
|
|
11
|
+
skipSyntheticDefault: boolean;
|
|
12
|
+
}): boolean;
|
|
13
|
+
export declare const __loaderInternals: {
|
|
14
|
+
buildSanitizedQuery: typeof buildSanitizedQuery;
|
|
15
|
+
shouldEmitCombinedDefault: typeof shouldEmitCombinedDefault;
|
|
16
|
+
};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
export const COMBINED_QUERY_FLAG = 'combined';
|
|
2
|
+
export const NAMED_ONLY_QUERY_FLAGS = ['named-only', 'no-default'];
|
|
3
|
+
export function splitQuery(query) {
|
|
4
|
+
const trimmed = query.startsWith('?') ? query.slice(1) : query;
|
|
5
|
+
if (!trimmed)
|
|
6
|
+
return [];
|
|
7
|
+
return trimmed.split('&').filter(Boolean);
|
|
8
|
+
}
|
|
9
|
+
export function isQueryFlag(entry, flag) {
|
|
10
|
+
const [rawKey] = entry.split('=');
|
|
11
|
+
try {
|
|
12
|
+
return decodeURIComponent(rawKey) === flag;
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return rawKey === flag;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export function buildSanitizedQuery(query) {
|
|
19
|
+
if (!query)
|
|
20
|
+
return '';
|
|
21
|
+
const entries = splitQuery(query).filter(part => {
|
|
22
|
+
if (isQueryFlag(part, COMBINED_QUERY_FLAG)) {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
if (isQueryFlag(part, 'knighted-css')) {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
if (NAMED_ONLY_QUERY_FLAGS.some(flag => isQueryFlag(part, flag))) {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
return true;
|
|
32
|
+
});
|
|
33
|
+
return entries.length > 0 ? `?${entries.join('&')}` : '';
|
|
34
|
+
}
|
|
35
|
+
export function shouldForwardDefaultExport(request) {
|
|
36
|
+
const [pathPart] = request.split('?');
|
|
37
|
+
if (!pathPart)
|
|
38
|
+
return true;
|
|
39
|
+
const lower = pathPart.toLowerCase();
|
|
40
|
+
if (lower.endsWith('.css.ts') || lower.endsWith('.css.js')) {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
export function shouldEmitCombinedDefault(options) {
|
|
46
|
+
if (options.skipSyntheticDefault) {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
if (!shouldForwardDefaultExport(options.request)) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
if (options.detection === 'has-default') {
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
if (options.detection === 'no-default') {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
export const __loaderInternals = {
|
|
61
|
+
buildSanitizedQuery,
|
|
62
|
+
shouldEmitCombinedDefault,
|
|
63
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { parse } from 'es-module-lexer';
|
|
2
|
+
export type ModuleDefaultSignal = 'has-default' | 'no-default' | 'unknown';
|
|
3
|
+
type LexerOverrides = {
|
|
4
|
+
parse?: typeof parse;
|
|
5
|
+
};
|
|
6
|
+
export declare function detectModuleDefaultExport(filePath: string): Promise<ModuleDefaultSignal>;
|
|
7
|
+
export declare const __moduleInfoInternals: {
|
|
8
|
+
setLexerOverrides(overrides?: LexerOverrides): void;
|
|
9
|
+
};
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { init, parse } from 'es-module-lexer';
|
|
4
|
+
const DETECTABLE_EXTENSIONS = new Set([
|
|
5
|
+
'.js',
|
|
6
|
+
'.jsx',
|
|
7
|
+
'.ts',
|
|
8
|
+
'.tsx',
|
|
9
|
+
'.mjs',
|
|
10
|
+
'.mts',
|
|
11
|
+
'.cjs',
|
|
12
|
+
'.cts',
|
|
13
|
+
]);
|
|
14
|
+
let lexerInit;
|
|
15
|
+
let lexerOverrides;
|
|
16
|
+
function ensureLexerInitialized() {
|
|
17
|
+
if (!lexerInit) {
|
|
18
|
+
lexerInit = init;
|
|
19
|
+
}
|
|
20
|
+
return lexerInit;
|
|
21
|
+
}
|
|
22
|
+
export async function detectModuleDefaultExport(filePath) {
|
|
23
|
+
if (!DETECTABLE_EXTENSIONS.has(path.extname(filePath))) {
|
|
24
|
+
return 'unknown';
|
|
25
|
+
}
|
|
26
|
+
let source;
|
|
27
|
+
try {
|
|
28
|
+
source = await readFile(filePath, 'utf8');
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return 'unknown';
|
|
32
|
+
}
|
|
33
|
+
try {
|
|
34
|
+
await ensureLexerInitialized();
|
|
35
|
+
const [, exports] = (lexerOverrides?.parse ?? parse)(source, filePath);
|
|
36
|
+
if (exports.some(entry => entry.n === 'default')) {
|
|
37
|
+
return 'has-default';
|
|
38
|
+
}
|
|
39
|
+
if (exports.length === 0) {
|
|
40
|
+
return 'unknown';
|
|
41
|
+
}
|
|
42
|
+
return 'no-default';
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return 'unknown';
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
export const __moduleInfoInternals = {
|
|
49
|
+
setLexerOverrides(overrides) {
|
|
50
|
+
lexerOverrides = overrides;
|
|
51
|
+
if (!overrides) {
|
|
52
|
+
lexerInit = undefined;
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export type CssResolver = (specifier: string, ctx: {
|
|
2
|
+
cwd: string;
|
|
3
|
+
}) => string | Promise<string | undefined>;
|
|
4
|
+
export declare function createSassImporter({ cwd, resolver, }: {
|
|
5
|
+
cwd: string;
|
|
6
|
+
resolver?: CssResolver;
|
|
7
|
+
}): {
|
|
8
|
+
canonicalize(url: string, context?: {
|
|
9
|
+
containingUrl?: URL | null;
|
|
10
|
+
}): Promise<import("node:url").URL | null>;
|
|
11
|
+
load(canonicalUrl: URL): Promise<{
|
|
12
|
+
contents: string;
|
|
13
|
+
syntax: "scss" | "indented";
|
|
14
|
+
}>;
|
|
15
|
+
} | undefined;
|
|
16
|
+
export declare function resolveAliasSpecifier(specifier: string, resolver: CssResolver, cwd: string): Promise<string | undefined>;
|
|
17
|
+
export declare function shouldNormalizeSpecifier(specifier: string): boolean;
|
|
18
|
+
export declare function ensureSassPath(filePath: string): string | undefined;
|
|
19
|
+
export declare function resolveRelativeSpecifier(specifier: string, containingUrl?: URL | null): string | undefined;
|
|
20
|
+
export declare const __sassInternals: {
|
|
21
|
+
createSassImporter: typeof createSassImporter;
|
|
22
|
+
resolveAliasSpecifier: typeof resolveAliasSpecifier;
|
|
23
|
+
shouldNormalizeSpecifier: typeof shouldNormalizeSpecifier;
|
|
24
|
+
ensureSassPath: typeof ensureSassPath;
|
|
25
|
+
resolveRelativeSpecifier: typeof resolveRelativeSpecifier;
|
|
26
|
+
};
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { existsSync, promises as fs } from 'node:fs';
|
|
3
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
4
|
+
export function createSassImporter({ cwd, resolver, }) {
|
|
5
|
+
if (!resolver)
|
|
6
|
+
return undefined;
|
|
7
|
+
const debug = process.env.KNIGHTED_CSS_DEBUG_SASS === '1';
|
|
8
|
+
return {
|
|
9
|
+
async canonicalize(url, context) {
|
|
10
|
+
if (debug) {
|
|
11
|
+
console.error('[knighted-css:sass] canonicalize request:', url);
|
|
12
|
+
if (context?.containingUrl) {
|
|
13
|
+
console.error('[knighted-css:sass] containing url:', context.containingUrl.href);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
if (shouldNormalizeSpecifier(url)) {
|
|
17
|
+
const resolvedPath = await resolveAliasSpecifier(url, resolver, cwd);
|
|
18
|
+
if (!resolvedPath) {
|
|
19
|
+
if (debug) {
|
|
20
|
+
console.error('[knighted-css:sass] resolver returned no result for', url);
|
|
21
|
+
}
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
const fileUrl = pathToFileURL(resolvedPath);
|
|
25
|
+
if (debug) {
|
|
26
|
+
console.error('[knighted-css:sass] canonical url:', fileUrl.href);
|
|
27
|
+
}
|
|
28
|
+
return fileUrl;
|
|
29
|
+
}
|
|
30
|
+
const relativePath = resolveRelativeSpecifier(url, context?.containingUrl);
|
|
31
|
+
if (relativePath) {
|
|
32
|
+
const fileUrl = pathToFileURL(relativePath);
|
|
33
|
+
if (debug) {
|
|
34
|
+
console.error('[knighted-css:sass] canonical url:', fileUrl.href);
|
|
35
|
+
}
|
|
36
|
+
return fileUrl;
|
|
37
|
+
}
|
|
38
|
+
return null;
|
|
39
|
+
},
|
|
40
|
+
async load(canonicalUrl) {
|
|
41
|
+
if (debug) {
|
|
42
|
+
console.error('[knighted-css:sass] load request:', canonicalUrl.href);
|
|
43
|
+
}
|
|
44
|
+
const filePath = fileURLToPath(canonicalUrl);
|
|
45
|
+
const contents = await fs.readFile(filePath, 'utf8');
|
|
46
|
+
return {
|
|
47
|
+
contents,
|
|
48
|
+
syntax: inferSassSyntax(filePath),
|
|
49
|
+
};
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
export async function resolveAliasSpecifier(specifier, resolver, cwd) {
|
|
54
|
+
const resolved = await resolver(specifier, { cwd });
|
|
55
|
+
if (!resolved) {
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
if (resolved.startsWith('file://')) {
|
|
59
|
+
return ensureSassPath(fileURLToPath(new URL(resolved)));
|
|
60
|
+
}
|
|
61
|
+
const normalized = path.isAbsolute(resolved) ? resolved : path.resolve(cwd, resolved);
|
|
62
|
+
return ensureSassPath(normalized);
|
|
63
|
+
}
|
|
64
|
+
export function shouldNormalizeSpecifier(specifier) {
|
|
65
|
+
const schemeMatch = specifier.match(/^([a-z][\w+.-]*):/i);
|
|
66
|
+
if (!schemeMatch) {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
const scheme = schemeMatch[1].toLowerCase();
|
|
70
|
+
if (scheme === 'file' ||
|
|
71
|
+
scheme === 'http' ||
|
|
72
|
+
scheme === 'https' ||
|
|
73
|
+
scheme === 'data' ||
|
|
74
|
+
scheme === 'sass') {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
function inferSassSyntax(filePath) {
|
|
80
|
+
return filePath.endsWith('.sass') ? 'indented' : 'scss';
|
|
81
|
+
}
|
|
82
|
+
export function ensureSassPath(filePath) {
|
|
83
|
+
if (existsSync(filePath)) {
|
|
84
|
+
return filePath;
|
|
85
|
+
}
|
|
86
|
+
const ext = path.extname(filePath);
|
|
87
|
+
const dir = path.dirname(filePath);
|
|
88
|
+
const base = path.basename(filePath, ext);
|
|
89
|
+
const partialCandidate = path.join(dir, `_${base}${ext}`);
|
|
90
|
+
if (ext && existsSync(partialCandidate)) {
|
|
91
|
+
return partialCandidate;
|
|
92
|
+
}
|
|
93
|
+
const indexCandidate = path.join(dir, base, `index${ext}`);
|
|
94
|
+
if (ext && existsSync(indexCandidate)) {
|
|
95
|
+
return indexCandidate;
|
|
96
|
+
}
|
|
97
|
+
const partialIndexCandidate = path.join(dir, base, `_index${ext}`);
|
|
98
|
+
if (ext && existsSync(partialIndexCandidate)) {
|
|
99
|
+
return partialIndexCandidate;
|
|
100
|
+
}
|
|
101
|
+
return undefined;
|
|
102
|
+
}
|
|
103
|
+
export function resolveRelativeSpecifier(specifier, containingUrl) {
|
|
104
|
+
if (!containingUrl || containingUrl.protocol !== 'file:') {
|
|
105
|
+
return undefined;
|
|
106
|
+
}
|
|
107
|
+
if (/^[a-z][\w+.-]*:/i.test(specifier)) {
|
|
108
|
+
return undefined;
|
|
109
|
+
}
|
|
110
|
+
const containingPath = fileURLToPath(containingUrl);
|
|
111
|
+
const baseDir = path.dirname(containingPath);
|
|
112
|
+
const candidate = path.resolve(baseDir, specifier);
|
|
113
|
+
return ensureSassPath(candidate);
|
|
114
|
+
}
|
|
115
|
+
export const __sassInternals = {
|
|
116
|
+
createSassImporter,
|
|
117
|
+
resolveAliasSpecifier,
|
|
118
|
+
shouldNormalizeSpecifier,
|
|
119
|
+
ensureSassPath,
|
|
120
|
+
resolveRelativeSpecifier,
|
|
121
|
+
};
|
package/loader-queries.d.ts
CHANGED
|
@@ -19,3 +19,15 @@ declare module '*?knighted-css&combined' {
|
|
|
19
19
|
export default combined
|
|
20
20
|
export const knightedCss: string
|
|
21
21
|
}
|
|
22
|
+
|
|
23
|
+
declare module '*?knighted-css&combined&named-only' {
|
|
24
|
+
const combined: KnightedCssCombinedModule<Record<string, unknown>>
|
|
25
|
+
export default combined
|
|
26
|
+
export const knightedCss: string
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
declare module '*?knighted-css&combined&no-default' {
|
|
30
|
+
const combined: KnightedCssCombinedModule<Record<string, unknown>>
|
|
31
|
+
export default combined
|
|
32
|
+
export const knightedCss: string
|
|
33
|
+
}
|
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.7",
|
|
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",
|
|
@@ -66,6 +66,7 @@
|
|
|
66
66
|
},
|
|
67
67
|
"dependencies": {
|
|
68
68
|
"dependency-tree": "^11.2.0",
|
|
69
|
+
"es-module-lexer": "^2.0.0",
|
|
69
70
|
"lightningcss": "^1.30.2"
|
|
70
71
|
},
|
|
71
72
|
"overrides": {
|