@knighted/css 1.0.0-rc.4 → 1.0.0-rc.5
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 +123 -4
- package/dist/css.js +123 -4
- package/package.json +1 -1
package/dist/cjs/css.cjs
CHANGED
|
@@ -9,6 +9,7 @@ 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");
|
|
12
13
|
const dependency_tree_1 = __importDefault(require("dependency-tree"));
|
|
13
14
|
const lightningcss_1 = require("lightningcss");
|
|
14
15
|
const helpers_js_1 = require("./helpers.cjs");
|
|
@@ -35,6 +36,7 @@ async function cssWithMeta(entry, options = {}) {
|
|
|
35
36
|
const chunk = await compileStyleModule(file, {
|
|
36
37
|
cwd,
|
|
37
38
|
peerResolver: options.peerResolver,
|
|
39
|
+
resolver: options.resolver,
|
|
38
40
|
});
|
|
39
41
|
if (chunk) {
|
|
40
42
|
chunks.push(chunk);
|
|
@@ -105,13 +107,17 @@ function matchExtension(filePath, extensions) {
|
|
|
105
107
|
const lower = filePath.toLowerCase();
|
|
106
108
|
return extensions.find(ext => lower.endsWith(ext));
|
|
107
109
|
}
|
|
108
|
-
async function compileStyleModule(file, { cwd, peerResolver }) {
|
|
110
|
+
async function compileStyleModule(file, { cwd, peerResolver, resolver, }) {
|
|
109
111
|
switch (file.ext) {
|
|
110
112
|
case '.css':
|
|
111
113
|
return node_fs_1.promises.readFile(file.path, 'utf8');
|
|
112
114
|
case '.scss':
|
|
113
115
|
case '.sass':
|
|
114
|
-
return compileSass(file.path, file.ext === '.sass',
|
|
116
|
+
return compileSass(file.path, file.ext === '.sass', {
|
|
117
|
+
cwd,
|
|
118
|
+
peerResolver,
|
|
119
|
+
resolver,
|
|
120
|
+
});
|
|
115
121
|
case '.less':
|
|
116
122
|
return compileLess(file.path, peerResolver);
|
|
117
123
|
case '.css.ts':
|
|
@@ -120,12 +126,14 @@ async function compileStyleModule(file, { cwd, peerResolver }) {
|
|
|
120
126
|
return '';
|
|
121
127
|
}
|
|
122
128
|
}
|
|
123
|
-
async function compileSass(filePath, indented, peerResolver) {
|
|
129
|
+
async function compileSass(filePath, indented, { cwd, peerResolver, resolver, }) {
|
|
124
130
|
const sassModule = await optionalPeer('sass', 'Sass', peerResolver);
|
|
125
131
|
const sass = sassModule;
|
|
126
|
-
const
|
|
132
|
+
const importer = createSassImporter({ cwd, resolver });
|
|
133
|
+
const result = await sass.compileAsync(filePath, {
|
|
127
134
|
style: 'expanded',
|
|
128
135
|
loadPaths: buildSassLoadPaths(filePath),
|
|
136
|
+
importers: importer ? [importer] : undefined,
|
|
129
137
|
});
|
|
130
138
|
return result.css;
|
|
131
139
|
}
|
|
@@ -146,6 +154,117 @@ function buildSassLoadPaths(filePath) {
|
|
|
146
154
|
loadPaths.add(node_path_1.default.join(cwd, 'node_modules'));
|
|
147
155
|
return Array.from(loadPaths).filter(dir => dir && (0, node_fs_1.existsSync)(dir));
|
|
148
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
|
+
}
|
|
149
268
|
async function compileLess(filePath, peerResolver) {
|
|
150
269
|
const mod = await optionalPeer('less', 'Less', peerResolver);
|
|
151
270
|
const less = unwrapModuleNamespace(mod);
|
package/dist/css.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import { existsSync, promises as fs } from 'node:fs';
|
|
3
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
3
4
|
import dependencyTree from 'dependency-tree';
|
|
4
5
|
import { composeVisitors, transform as lightningTransform, } from 'lightningcss';
|
|
5
6
|
import { applyStringSpecificityBoost, buildSpecificityVisitor, } from './helpers.js';
|
|
@@ -26,6 +27,7 @@ export async function cssWithMeta(entry, options = {}) {
|
|
|
26
27
|
const chunk = await compileStyleModule(file, {
|
|
27
28
|
cwd,
|
|
28
29
|
peerResolver: options.peerResolver,
|
|
30
|
+
resolver: options.resolver,
|
|
29
31
|
});
|
|
30
32
|
if (chunk) {
|
|
31
33
|
chunks.push(chunk);
|
|
@@ -96,13 +98,17 @@ function matchExtension(filePath, extensions) {
|
|
|
96
98
|
const lower = filePath.toLowerCase();
|
|
97
99
|
return extensions.find(ext => lower.endsWith(ext));
|
|
98
100
|
}
|
|
99
|
-
async function compileStyleModule(file, { cwd, peerResolver }) {
|
|
101
|
+
async function compileStyleModule(file, { cwd, peerResolver, resolver, }) {
|
|
100
102
|
switch (file.ext) {
|
|
101
103
|
case '.css':
|
|
102
104
|
return fs.readFile(file.path, 'utf8');
|
|
103
105
|
case '.scss':
|
|
104
106
|
case '.sass':
|
|
105
|
-
return compileSass(file.path, file.ext === '.sass',
|
|
107
|
+
return compileSass(file.path, file.ext === '.sass', {
|
|
108
|
+
cwd,
|
|
109
|
+
peerResolver,
|
|
110
|
+
resolver,
|
|
111
|
+
});
|
|
106
112
|
case '.less':
|
|
107
113
|
return compileLess(file.path, peerResolver);
|
|
108
114
|
case '.css.ts':
|
|
@@ -111,12 +117,14 @@ async function compileStyleModule(file, { cwd, peerResolver }) {
|
|
|
111
117
|
return '';
|
|
112
118
|
}
|
|
113
119
|
}
|
|
114
|
-
async function compileSass(filePath, indented, peerResolver) {
|
|
120
|
+
async function compileSass(filePath, indented, { cwd, peerResolver, resolver, }) {
|
|
115
121
|
const sassModule = await optionalPeer('sass', 'Sass', peerResolver);
|
|
116
122
|
const sass = sassModule;
|
|
117
|
-
const
|
|
123
|
+
const importer = createSassImporter({ cwd, resolver });
|
|
124
|
+
const result = await sass.compileAsync(filePath, {
|
|
118
125
|
style: 'expanded',
|
|
119
126
|
loadPaths: buildSassLoadPaths(filePath),
|
|
127
|
+
importers: importer ? [importer] : undefined,
|
|
120
128
|
});
|
|
121
129
|
return result.css;
|
|
122
130
|
}
|
|
@@ -137,6 +145,117 @@ function buildSassLoadPaths(filePath) {
|
|
|
137
145
|
loadPaths.add(path.join(cwd, 'node_modules'));
|
|
138
146
|
return Array.from(loadPaths).filter(dir => dir && existsSync(dir));
|
|
139
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
|
+
}
|
|
140
259
|
async function compileLess(filePath, peerResolver) {
|
|
141
260
|
const mod = await optionalPeer('less', 'Less', peerResolver);
|
|
142
261
|
const less = unwrapModuleNamespace(mod);
|
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.5",
|
|
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",
|