@parcel/transformer-stylus 2.16.4 → 3.0.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/StylusTransformer.js +155 -314
- package/package.json +12 -7
- package/LICENSE +0 -21
- package/src/StylusTransformer.js +0 -352
package/lib/StylusTransformer.js
CHANGED
|
@@ -1,330 +1,171 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
return data;
|
|
11
|
-
};
|
|
12
|
-
return data;
|
|
13
|
-
}
|
|
14
|
-
function _utils() {
|
|
15
|
-
const data = require("@parcel/utils");
|
|
16
|
-
_utils = function () {
|
|
17
|
-
return data;
|
|
18
|
-
};
|
|
19
|
-
return data;
|
|
20
|
-
}
|
|
21
|
-
function _path() {
|
|
22
|
-
const data = _interopRequireDefault(require("path"));
|
|
23
|
-
_path = function () {
|
|
24
|
-
return data;
|
|
25
|
-
};
|
|
26
|
-
return data;
|
|
27
|
-
}
|
|
28
|
-
function _fs() {
|
|
29
|
-
const data = _interopRequireDefault(require("fs"));
|
|
30
|
-
_fs = function () {
|
|
31
|
-
return data;
|
|
32
|
-
};
|
|
33
|
-
return data;
|
|
34
|
-
}
|
|
35
|
-
function _stylus() {
|
|
36
|
-
const data = _interopRequireDefault(require("stylus"));
|
|
37
|
-
_stylus = function () {
|
|
38
|
-
return data;
|
|
39
|
-
};
|
|
40
|
-
return data;
|
|
41
|
-
}
|
|
42
|
-
function _parser() {
|
|
43
|
-
const data = _interopRequireDefault(require("stylus/lib/parser"));
|
|
44
|
-
_parser = function () {
|
|
45
|
-
return data;
|
|
46
|
-
};
|
|
47
|
-
return data;
|
|
48
|
-
}
|
|
49
|
-
function _depsResolver() {
|
|
50
|
-
const data = _interopRequireDefault(require("stylus/lib/visitor/deps-resolver"));
|
|
51
|
-
_depsResolver = function () {
|
|
52
|
-
return data;
|
|
53
|
-
};
|
|
54
|
-
return data;
|
|
55
|
-
}
|
|
56
|
-
function _nodes() {
|
|
57
|
-
const data = _interopRequireDefault(require("stylus/lib/nodes"));
|
|
58
|
-
_nodes = function () {
|
|
59
|
-
return data;
|
|
60
|
-
};
|
|
61
|
-
return data;
|
|
62
|
-
}
|
|
63
|
-
function _utils2() {
|
|
64
|
-
const data = _interopRequireDefault(require("stylus/lib/utils"));
|
|
65
|
-
_utils2 = function () {
|
|
66
|
-
return data;
|
|
67
|
-
};
|
|
68
|
-
return data;
|
|
69
|
-
}
|
|
70
|
-
function _evaluator() {
|
|
71
|
-
const data = _interopRequireDefault(require("stylus/lib/visitor/evaluator"));
|
|
72
|
-
_evaluator = function () {
|
|
73
|
-
return data;
|
|
74
|
-
};
|
|
75
|
-
return data;
|
|
76
|
-
}
|
|
77
|
-
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
1
|
+
import { Transformer } from '@parcel/plugin';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import nativeFS from 'fs';
|
|
4
|
+
import stylus from 'stylus';
|
|
5
|
+
import Parser from 'stylus/lib/parser';
|
|
6
|
+
import DepsResolver from 'stylus/lib/visitor/deps-resolver';
|
|
7
|
+
import nodes from 'stylus/lib/nodes/index';
|
|
8
|
+
import * as utils from 'stylus/lib/utils';
|
|
9
|
+
import Evaluator from 'stylus/lib/visitor/evaluator';
|
|
78
10
|
const URL_RE = /^(?:url\s*\(\s*)?['"]?(?:[#/]|(?:https?:)?\/\/)/i;
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
11
|
+
export default new Transformer({
|
|
12
|
+
async transform ({ asset, resolve, config, options }) {
|
|
13
|
+
let stylusConfig = {
|
|
14
|
+
...config
|
|
15
|
+
};
|
|
16
|
+
if (Array.isArray(stylusConfig.paths)) {
|
|
17
|
+
stylusConfig.paths = stylusConfig.paths.map((p)=>path.join(options.projectRoot, p));
|
|
18
|
+
}
|
|
19
|
+
let code = asset.getCode();
|
|
20
|
+
let style = stylus(code, stylusConfig);
|
|
21
|
+
style.set('filename', asset.filePath);
|
|
22
|
+
style.set('include css', true);
|
|
23
|
+
// Rewrite relative url()s in imported files to be relative to the entry file,
|
|
24
|
+
// so the CSS transformer resolves them correctly after compilation.
|
|
25
|
+
style.define('url', stylus.resolver({}));
|
|
26
|
+
style.set('Evaluator', await createEvaluator(code, asset, resolve, style.options));
|
|
27
|
+
asset.type = 'css';
|
|
28
|
+
asset.setCode(style.render());
|
|
92
29
|
}
|
|
93
|
-
},
|
|
94
|
-
async transform({
|
|
95
|
-
asset,
|
|
96
|
-
resolve,
|
|
97
|
-
config,
|
|
98
|
-
options
|
|
99
|
-
}) {
|
|
100
|
-
let code = await asset.getCode();
|
|
101
|
-
let style = (0, _stylus().default)(code, {
|
|
102
|
-
...(config ?? {})
|
|
103
|
-
});
|
|
104
|
-
style.set('filename', asset.filePath);
|
|
105
|
-
style.set('include css', true);
|
|
106
|
-
// Setup a handler for the URL function so we add dependencies for linked assets.
|
|
107
|
-
style.define('url', node => {
|
|
108
|
-
let filename = asset.addURLDependency(node.val, {
|
|
109
|
-
loc: (0, _utils().createDependencyLocation)({
|
|
110
|
-
line: node.lineno,
|
|
111
|
-
column: node.column
|
|
112
|
-
}, node.val)
|
|
113
|
-
});
|
|
114
|
-
return new (_stylus().default.nodes.Literal)(`url(${JSON.stringify(filename)})`);
|
|
115
|
-
});
|
|
116
|
-
let {
|
|
117
|
-
resolved: stylusPath
|
|
118
|
-
} = await options.packageManager.resolve('stylus', __filename);
|
|
119
|
-
let nativeGlob = await options.packageManager.require('glob', stylusPath);
|
|
120
|
-
style.set('Evaluator', await createEvaluator(code, asset, resolve, style.options, options, nativeGlob));
|
|
121
|
-
asset.type = 'css';
|
|
122
|
-
asset.setCode(style.render());
|
|
123
|
-
asset.meta.hasDependencies = false;
|
|
124
|
-
return [asset];
|
|
125
|
-
}
|
|
126
30
|
});
|
|
127
|
-
function attemptResolve(importedPath, filepath,
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
}
|
|
131
|
-
if ((0, _utils().isGlob)(importedPath)) {
|
|
132
|
-
// Invalidate when new files are created that match the glob pattern.
|
|
133
|
-
let absoluteGlob = _path().default.resolve(_path().default.dirname(filepath), importedPath);
|
|
134
|
-
asset.invalidateOnFileCreate({
|
|
135
|
-
glob: absoluteGlob
|
|
136
|
-
});
|
|
137
|
-
deps.set(importedPath, (0, _utils().glob)(absoluteGlob, asset.fs, {
|
|
138
|
-
onlyFiles: true
|
|
139
|
-
}).then(entries => Promise.all(entries.map(entry => resolve(filepath, './' + _path().default.relative(_path().default.dirname(filepath), entry), {
|
|
140
|
-
packageConditions: ['stylus', 'style']
|
|
141
|
-
})))));
|
|
142
|
-
} else {
|
|
143
|
-
deps.set(importedPath, resolve(filepath, importedPath, {
|
|
144
|
-
packageConditions: ['stylus', 'style']
|
|
145
|
-
}));
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
async function getDependencies(code, filepath, asset, resolve, options, parcelOptions, nativeGlob, seen = new Set(), includeImports = true) {
|
|
149
|
-
seen.add(filepath);
|
|
150
|
-
_nodes().default.filename = asset.filePath;
|
|
151
|
-
let parser = new (_parser().default)(code, options);
|
|
152
|
-
let ast = parser.parse();
|
|
153
|
-
let deps = new Map();
|
|
154
|
-
if (includeImports && options.imports) {
|
|
155
|
-
for (let importedPath of options.imports) {
|
|
156
|
-
attemptResolve(importedPath, filepath, asset, resolve, deps);
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
class ImportVisitor extends _depsResolver().default {
|
|
160
|
-
visitImport(imported) {
|
|
161
|
-
let importedPath = imported.path.first.string;
|
|
162
|
-
attemptResolve(importedPath, filepath, asset, resolve, deps);
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
new ImportVisitor(ast, options).visit(ast);
|
|
166
|
-
|
|
167
|
-
// Recursively process depdendencies, and return a map with all resolved paths.
|
|
168
|
-
let res = new Map();
|
|
169
|
-
await Promise.all(Array.from(deps.entries()).map(async ([importedPath, resolved]) => {
|
|
170
|
-
try {
|
|
171
|
-
resolved = await resolved;
|
|
172
|
-
} catch (err) {
|
|
173
|
-
resolved = null;
|
|
31
|
+
function attemptResolve(importedPath, filepath, resolve, deps) {
|
|
32
|
+
if (deps.has(importedPath)) {
|
|
33
|
+
return;
|
|
174
34
|
}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
35
|
+
if (/[*[{]/.test(importedPath)) {
|
|
36
|
+
// Invalidate when new files are created that match the glob pattern.
|
|
37
|
+
// let absoluteGlob = path.resolve(path.dirname(filepath), importedPath);
|
|
38
|
+
// asset.invalidateOnFileCreate({glob: absoluteGlob});
|
|
39
|
+
let cwd = path.dirname(filepath);
|
|
40
|
+
let entries = nativeFS.globSync(importedPath, {
|
|
41
|
+
cwd
|
|
42
|
+
});
|
|
43
|
+
deps.set(importedPath, Promise.all(entries.filter((e)=>nativeFS.statSync(path.join(cwd, e)).isFile()).map((entry)=>resolveStylesheet(resolve, filepath, './' + entry))));
|
|
179
44
|
} else {
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
// support optional .styl
|
|
184
|
-
let originalPath = importedPath;
|
|
185
|
-
if (!/\.styl$/i.test(importedPath)) {
|
|
186
|
-
importedPath += '.styl';
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
// Patch the native FS so we use Parcel's FS, and track files that are
|
|
190
|
-
// checked so we invalidate the cache when they are created.
|
|
191
|
-
let restore = patchNativeFS(asset.fs, nativeGlob);
|
|
192
|
-
let paths = [...new Set((options.paths || []).concat(_path().default.dirname(filepath || '.')))];
|
|
193
|
-
found = _utils2().default.find(importedPath, paths, filepath);
|
|
194
|
-
if (!found) {
|
|
195
|
-
found = _utils2().default.lookupIndex(originalPath, paths, filepath);
|
|
196
|
-
}
|
|
197
|
-
for (let invalidation of restore()) {
|
|
198
|
-
asset.invalidateOnFileCreate(invalidation);
|
|
199
|
-
}
|
|
200
|
-
if (!found) {
|
|
201
|
-
throw new Error('failed to locate file ' + originalPath);
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
// Recursively process resolved files as well to get nested deps
|
|
206
|
-
for (let resolved of found) {
|
|
207
|
-
if (!seen.has(resolved)) {
|
|
208
|
-
asset.invalidateOnFileChange(resolved);
|
|
209
|
-
let code = await asset.fs.readFile(resolved, 'utf8');
|
|
210
|
-
for (let [path, resolvedPath] of await getDependencies(code, resolved, asset, resolve, options, parcelOptions, nativeGlob, seen, false)) {
|
|
211
|
-
res.set(path, resolvedPath);
|
|
212
|
-
}
|
|
213
|
-
}
|
|
45
|
+
let relative = path.isAbsolute(importedPath) ? './' + path.relative(path.dirname(filepath), importedPath) : importedPath;
|
|
46
|
+
deps.set(importedPath, resolveStylesheet(resolve, filepath, relative));
|
|
214
47
|
}
|
|
215
|
-
}));
|
|
216
|
-
return res;
|
|
217
48
|
}
|
|
218
|
-
async function
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
if (node.name !== 'url' && path && !URL_RE.test(path)) {
|
|
229
|
-
let resolved = deps.get(path);
|
|
230
|
-
|
|
231
|
-
// First try resolving using the node require resolution algorithm.
|
|
232
|
-
// This allows stylus files in node_modules to be resolved properly.
|
|
233
|
-
// If we find something, update the AST so stylus gets the absolute path to load later.
|
|
234
|
-
if (resolved) {
|
|
235
|
-
if (!Array.isArray(resolved)) {
|
|
236
|
-
node.string = resolved;
|
|
237
|
-
} else {
|
|
238
|
-
// If the import resolves to multiple files (i.e. glob),
|
|
239
|
-
// replace it with a separate import node for each file
|
|
240
|
-
return mergeBlocks(resolved.map(resolvedPath => {
|
|
241
|
-
node.string = resolvedPath;
|
|
242
|
-
let restore = patchNativeFS(asset.fs, nativeGlob);
|
|
243
|
-
let res = super.visitImport(imported.clone());
|
|
244
|
-
restore();
|
|
245
|
-
return res;
|
|
246
|
-
}));
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
// Patch the native FS so stylus uses Parcel's FS to read the file.
|
|
252
|
-
let restore = patchNativeFS(asset.fs, nativeGlob);
|
|
253
|
-
|
|
254
|
-
// Done. Let stylus do its thing.
|
|
255
|
-
let res = super.visitImport(imported);
|
|
256
|
-
restore();
|
|
257
|
-
return res;
|
|
49
|
+
/** Resolves a stylesheet import to the path of a file, or rejects. */ async function resolveStylesheet(resolve, from, specifier) {
|
|
50
|
+
let resolved = await resolve(from, specifier, {
|
|
51
|
+
importType: 'stylesheet',
|
|
52
|
+
packageConditions: [
|
|
53
|
+
'stylus',
|
|
54
|
+
'style'
|
|
55
|
+
]
|
|
56
|
+
});
|
|
57
|
+
if (resolved?.filePath == null) {
|
|
58
|
+
throw new Error(`Could not resolve '${specifier}' to a file`);
|
|
258
59
|
}
|
|
259
|
-
|
|
260
|
-
return CustomEvaluator;
|
|
60
|
+
return resolved.filePath;
|
|
261
61
|
}
|
|
262
|
-
function
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
// $FlowFixMe
|
|
273
|
-
_fs().default.statSync = p => {
|
|
274
|
-
try {
|
|
275
|
-
return fs.statSync(p);
|
|
276
|
-
} catch (err) {
|
|
277
|
-
// Track files that were checked but don't exist so that we watch for their creation.
|
|
278
|
-
if (!p.includes(`node_modules${_path().default.sep}stylus`)) {
|
|
279
|
-
invalidations.push({
|
|
280
|
-
filePath: p
|
|
281
|
-
});
|
|
282
|
-
}
|
|
283
|
-
throw err;
|
|
62
|
+
async function getDependencies(code, filepath, asset, resolve, options, seen = new Set(), includeImports = true) {
|
|
63
|
+
seen.add(filepath);
|
|
64
|
+
nodes.filename = asset.filePath;
|
|
65
|
+
let parser = new Parser(code, options);
|
|
66
|
+
let ast = parser.parse();
|
|
67
|
+
let deps = new Map();
|
|
68
|
+
if (includeImports && options.imports) {
|
|
69
|
+
for (let importedPath of options.imports){
|
|
70
|
+
attemptResolve(importedPath, filepath, resolve, deps);
|
|
71
|
+
}
|
|
284
72
|
}
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
let res = (0, _utils().globSync)(p, fs);
|
|
291
|
-
if (!p.includes(`node_modules${_path().default.sep}stylus`)) {
|
|
292
|
-
// Sometimes stylus passes file paths with no glob parts to the `glob` module.
|
|
293
|
-
// We want to avoid treating these as globs for performance.
|
|
294
|
-
if ((0, _utils().isGlob)(p)) {
|
|
295
|
-
invalidations.push({
|
|
296
|
-
glob: p
|
|
297
|
-
});
|
|
298
|
-
} else if (res.length === 0) {
|
|
299
|
-
invalidations.push({
|
|
300
|
-
filePath: p
|
|
301
|
-
});
|
|
302
|
-
}
|
|
73
|
+
class ImportVisitor extends DepsResolver {
|
|
74
|
+
visitImport(imported) {
|
|
75
|
+
let importedPath = imported.path.first.string;
|
|
76
|
+
attemptResolve(importedPath, filepath, resolve, deps);
|
|
77
|
+
}
|
|
303
78
|
}
|
|
79
|
+
new ImportVisitor(ast, options).visit(ast);
|
|
80
|
+
// Recursively process depdendencies, and return a map with all resolved paths.
|
|
81
|
+
let res = new Map();
|
|
82
|
+
await Promise.all(Array.from(deps.entries()).map(async ([importedPath, promise])=>{
|
|
83
|
+
let resolved;
|
|
84
|
+
try {
|
|
85
|
+
resolved = await promise;
|
|
86
|
+
} catch (err) {
|
|
87
|
+
resolved = null;
|
|
88
|
+
}
|
|
89
|
+
let found;
|
|
90
|
+
if (resolved && (!Array.isArray(resolved) || resolved.length > 0)) {
|
|
91
|
+
found = Array.isArray(resolved) ? resolved : [
|
|
92
|
+
resolved
|
|
93
|
+
];
|
|
94
|
+
res.set(importedPath, resolved);
|
|
95
|
+
} else {
|
|
96
|
+
// If we couldn't resolve, try the normal stylus resolver.
|
|
97
|
+
// We just need to do this to keep track of the dependencies - stylus does the real work.
|
|
98
|
+
// support optional .styl
|
|
99
|
+
let originalPath = importedPath;
|
|
100
|
+
if (!/\.styl$/i.test(importedPath)) {
|
|
101
|
+
importedPath += '.styl';
|
|
102
|
+
}
|
|
103
|
+
let paths = [
|
|
104
|
+
...new Set((options.paths || []).concat(path.dirname(filepath || '.')))
|
|
105
|
+
];
|
|
106
|
+
found = utils.find(importedPath, paths, filepath);
|
|
107
|
+
if (!found) {
|
|
108
|
+
found = utils.lookupIndex(originalPath, paths, filepath);
|
|
109
|
+
}
|
|
110
|
+
if (!found) {
|
|
111
|
+
throw new Error('failed to locate file ' + originalPath);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
// Recursively process resolved files as well to get nested deps
|
|
115
|
+
for (let resolved of found){
|
|
116
|
+
if (!seen.has(resolved)) {
|
|
117
|
+
let code = nativeFS.readFileSync(resolved, 'utf8');
|
|
118
|
+
for (let [path, resolvedPath] of (await getDependencies(code, resolved, asset, resolve, options, seen, false))){
|
|
119
|
+
res.set(path, resolvedPath);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}));
|
|
304
124
|
return res;
|
|
305
|
-
};
|
|
306
|
-
return () => {
|
|
307
|
-
// $FlowFixMe
|
|
308
|
-
_fs().default.readFileSync = readFileSync;
|
|
309
|
-
// $FlowFixMe
|
|
310
|
-
_fs().default.statSync = statSync;
|
|
311
|
-
nativeGlob.sync = glob;
|
|
312
|
-
return invalidations;
|
|
313
|
-
};
|
|
314
125
|
}
|
|
315
|
-
|
|
126
|
+
async function createEvaluator(code, asset, resolve, options) {
|
|
127
|
+
const deps = await getDependencies(code, asset.filePath, asset, resolve, options);
|
|
128
|
+
// This is a custom stylus evaluator that extends stylus with support for the node
|
|
129
|
+
// require resolution algorithm. It also adds all dependencies to the parcel asset
|
|
130
|
+
// tree so the file watcher works correctly, etc.
|
|
131
|
+
class CustomEvaluator extends Evaluator {
|
|
132
|
+
visitImport(imported) {
|
|
133
|
+
let node = this.visit(imported.path).first;
|
|
134
|
+
let path = node.string;
|
|
135
|
+
if (node.name !== 'url' && path && !URL_RE.test(path)) {
|
|
136
|
+
let resolved = deps.get(path);
|
|
137
|
+
// First try resolving using the node require resolution algorithm.
|
|
138
|
+
// This allows stylus files in node_modules to be resolved properly.
|
|
139
|
+
// If we find something, update the AST so stylus gets the absolute path to load later.
|
|
140
|
+
if (resolved) {
|
|
141
|
+
if (!Array.isArray(resolved)) {
|
|
142
|
+
node.string = resolved;
|
|
143
|
+
} else {
|
|
144
|
+
// If the import resolves to multiple files (i.e. glob),
|
|
145
|
+
// replace it with a separate import node for each file
|
|
146
|
+
return mergeBlocks(resolved.map((resolvedPath)=>{
|
|
147
|
+
node.string = resolvedPath;
|
|
148
|
+
return super.visitImport(imported.clone());
|
|
149
|
+
}));
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
// Done. Let stylus do its thing.
|
|
154
|
+
return super.visitImport(imported);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return CustomEvaluator;
|
|
158
|
+
}
|
|
316
159
|
/**
|
|
317
160
|
* Puts the content of all given node blocks into the first one, essentially merging them.
|
|
318
|
-
*/
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
finalBlock = block;
|
|
161
|
+
*/ function mergeBlocks(blocks) {
|
|
162
|
+
let finalBlock;
|
|
163
|
+
for (const block of blocks){
|
|
164
|
+
if (finalBlock) {
|
|
165
|
+
block.nodes.forEach((node)=>finalBlock.push(node));
|
|
166
|
+
} else {
|
|
167
|
+
finalBlock = block;
|
|
168
|
+
}
|
|
327
169
|
}
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
}
|
|
170
|
+
return finalBlock;
|
|
171
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@parcel/transformer-stylus",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0-alpha.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -13,16 +13,21 @@
|
|
|
13
13
|
"type": "git",
|
|
14
14
|
"url": "https://github.com/parcel-bundler/parcel.git"
|
|
15
15
|
},
|
|
16
|
+
"type": "module",
|
|
16
17
|
"main": "lib/StylusTransformer.js",
|
|
17
|
-
"
|
|
18
|
+
"files": [
|
|
19
|
+
"lib"
|
|
20
|
+
],
|
|
21
|
+
"source": "src/StylusTransformer.ts",
|
|
18
22
|
"engines": {
|
|
19
23
|
"node": ">= 16.0.0",
|
|
20
|
-
"parcel": "^
|
|
24
|
+
"parcel": "^3.0.0-0"
|
|
21
25
|
},
|
|
22
26
|
"dependencies": {
|
|
23
|
-
"@parcel/plugin": "
|
|
24
|
-
"@parcel/utils": "2.16.4",
|
|
27
|
+
"@parcel/plugin": "^3.0.0-alpha.1",
|
|
25
28
|
"stylus": "^0.63.0"
|
|
26
29
|
},
|
|
27
|
-
"
|
|
28
|
-
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@types/stylus": "^0.48.43"
|
|
32
|
+
}
|
|
33
|
+
}
|
package/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2017-present Devon Govett
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
package/src/StylusTransformer.js
DELETED
|
@@ -1,352 +0,0 @@
|
|
|
1
|
-
// @flow
|
|
2
|
-
|
|
3
|
-
import {Transformer} from '@parcel/plugin';
|
|
4
|
-
import {createDependencyLocation, isGlob, glob, globSync} from '@parcel/utils';
|
|
5
|
-
import path from 'path';
|
|
6
|
-
import nativeFS from 'fs';
|
|
7
|
-
import stylus from 'stylus';
|
|
8
|
-
import Parser from 'stylus/lib/parser';
|
|
9
|
-
import DepsResolver from 'stylus/lib/visitor/deps-resolver';
|
|
10
|
-
import nodes from 'stylus/lib/nodes';
|
|
11
|
-
import utils from 'stylus/lib/utils';
|
|
12
|
-
import Evaluator from 'stylus/lib/visitor/evaluator';
|
|
13
|
-
|
|
14
|
-
const URL_RE = /^(?:url\s*\(\s*)?['"]?(?:[#/]|(?:https?:)?\/\/)/i;
|
|
15
|
-
|
|
16
|
-
export default (new Transformer({
|
|
17
|
-
async loadConfig({config}) {
|
|
18
|
-
let configFile = await config.getConfig(
|
|
19
|
-
['.stylusrc', '.stylusrc.js', '.stylusrc.cjs', '.stylusrc.mjs'],
|
|
20
|
-
{
|
|
21
|
-
packageKey: 'stylus',
|
|
22
|
-
},
|
|
23
|
-
);
|
|
24
|
-
|
|
25
|
-
if (configFile) {
|
|
26
|
-
// Resolve relative paths from config file
|
|
27
|
-
if (configFile.contents.paths) {
|
|
28
|
-
configFile.contents.paths = configFile.contents.paths.map(p =>
|
|
29
|
-
path.resolve(path.dirname(configFile.filePath), p),
|
|
30
|
-
);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
return configFile.contents;
|
|
34
|
-
}
|
|
35
|
-
},
|
|
36
|
-
|
|
37
|
-
async transform({asset, resolve, config, options}) {
|
|
38
|
-
let stylusConfig = config ?? {};
|
|
39
|
-
let code = await asset.getCode();
|
|
40
|
-
let style = stylus(code, {...stylusConfig});
|
|
41
|
-
style.set('filename', asset.filePath);
|
|
42
|
-
style.set('include css', true);
|
|
43
|
-
// Setup a handler for the URL function so we add dependencies for linked assets.
|
|
44
|
-
style.define('url', (node: stylus.nodes.String | stylus.nodes.Literal) => {
|
|
45
|
-
let filename = asset.addURLDependency(node.val, {
|
|
46
|
-
loc: createDependencyLocation(
|
|
47
|
-
{line: node.lineno, column: node.column},
|
|
48
|
-
node.val,
|
|
49
|
-
),
|
|
50
|
-
});
|
|
51
|
-
return new stylus.nodes.Literal(`url(${JSON.stringify(filename)})`);
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
let {resolved: stylusPath} = await options.packageManager.resolve(
|
|
55
|
-
'stylus',
|
|
56
|
-
__filename,
|
|
57
|
-
);
|
|
58
|
-
let nativeGlob = await options.packageManager.require('glob', stylusPath);
|
|
59
|
-
|
|
60
|
-
style.set(
|
|
61
|
-
'Evaluator',
|
|
62
|
-
await createEvaluator(
|
|
63
|
-
code,
|
|
64
|
-
asset,
|
|
65
|
-
resolve,
|
|
66
|
-
style.options,
|
|
67
|
-
options,
|
|
68
|
-
nativeGlob,
|
|
69
|
-
),
|
|
70
|
-
);
|
|
71
|
-
|
|
72
|
-
asset.type = 'css';
|
|
73
|
-
asset.setCode(style.render());
|
|
74
|
-
asset.meta.hasDependencies = false;
|
|
75
|
-
return [asset];
|
|
76
|
-
},
|
|
77
|
-
}): Transformer);
|
|
78
|
-
|
|
79
|
-
function attemptResolve(importedPath, filepath, asset, resolve, deps) {
|
|
80
|
-
if (deps.has(importedPath)) {
|
|
81
|
-
return;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
if (isGlob(importedPath)) {
|
|
85
|
-
// Invalidate when new files are created that match the glob pattern.
|
|
86
|
-
let absoluteGlob = path.resolve(path.dirname(filepath), importedPath);
|
|
87
|
-
asset.invalidateOnFileCreate({glob: absoluteGlob});
|
|
88
|
-
|
|
89
|
-
deps.set(
|
|
90
|
-
importedPath,
|
|
91
|
-
glob(absoluteGlob, asset.fs, {
|
|
92
|
-
onlyFiles: true,
|
|
93
|
-
}).then(entries =>
|
|
94
|
-
Promise.all(
|
|
95
|
-
entries.map(entry =>
|
|
96
|
-
resolve(
|
|
97
|
-
filepath,
|
|
98
|
-
'./' + path.relative(path.dirname(filepath), entry),
|
|
99
|
-
{
|
|
100
|
-
packageConditions: ['stylus', 'style'],
|
|
101
|
-
},
|
|
102
|
-
),
|
|
103
|
-
),
|
|
104
|
-
),
|
|
105
|
-
),
|
|
106
|
-
);
|
|
107
|
-
} else {
|
|
108
|
-
deps.set(
|
|
109
|
-
importedPath,
|
|
110
|
-
resolve(filepath, importedPath, {
|
|
111
|
-
packageConditions: ['stylus', 'style'],
|
|
112
|
-
}),
|
|
113
|
-
);
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
async function getDependencies(
|
|
118
|
-
code,
|
|
119
|
-
filepath,
|
|
120
|
-
asset,
|
|
121
|
-
resolve,
|
|
122
|
-
options,
|
|
123
|
-
parcelOptions,
|
|
124
|
-
nativeGlob,
|
|
125
|
-
seen = new Set(),
|
|
126
|
-
includeImports = true,
|
|
127
|
-
) {
|
|
128
|
-
seen.add(filepath);
|
|
129
|
-
|
|
130
|
-
nodes.filename = asset.filePath;
|
|
131
|
-
|
|
132
|
-
let parser = new Parser(code, options);
|
|
133
|
-
let ast = parser.parse();
|
|
134
|
-
let deps = new Map();
|
|
135
|
-
|
|
136
|
-
if (includeImports && options.imports) {
|
|
137
|
-
for (let importedPath of options.imports) {
|
|
138
|
-
attemptResolve(importedPath, filepath, asset, resolve, deps);
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
class ImportVisitor extends DepsResolver {
|
|
143
|
-
visitImport(imported) {
|
|
144
|
-
let importedPath = imported.path.first.string;
|
|
145
|
-
attemptResolve(importedPath, filepath, asset, resolve, deps);
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
new ImportVisitor(ast, options).visit(ast);
|
|
150
|
-
|
|
151
|
-
// Recursively process depdendencies, and return a map with all resolved paths.
|
|
152
|
-
let res = new Map();
|
|
153
|
-
await Promise.all(
|
|
154
|
-
Array.from(deps.entries()).map(async ([importedPath, resolved]) => {
|
|
155
|
-
try {
|
|
156
|
-
resolved = await resolved;
|
|
157
|
-
} catch (err) {
|
|
158
|
-
resolved = null;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
let found;
|
|
162
|
-
if (resolved && (!Array.isArray(resolved) || resolved.length > 0)) {
|
|
163
|
-
found = Array.isArray(resolved) ? resolved : [resolved];
|
|
164
|
-
res.set(importedPath, resolved);
|
|
165
|
-
} else {
|
|
166
|
-
// If we couldn't resolve, try the normal stylus resolver.
|
|
167
|
-
// We just need to do this to keep track of the dependencies - stylus does the real work.
|
|
168
|
-
|
|
169
|
-
// support optional .styl
|
|
170
|
-
let originalPath = importedPath;
|
|
171
|
-
if (!/\.styl$/i.test(importedPath)) {
|
|
172
|
-
importedPath += '.styl';
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
// Patch the native FS so we use Parcel's FS, and track files that are
|
|
176
|
-
// checked so we invalidate the cache when they are created.
|
|
177
|
-
let restore = patchNativeFS(asset.fs, nativeGlob);
|
|
178
|
-
|
|
179
|
-
let paths = [
|
|
180
|
-
...new Set(
|
|
181
|
-
(options.paths || []).concat(path.dirname(filepath || '.')),
|
|
182
|
-
),
|
|
183
|
-
];
|
|
184
|
-
found = utils.find(importedPath, paths, filepath);
|
|
185
|
-
if (!found) {
|
|
186
|
-
found = utils.lookupIndex(originalPath, paths, filepath);
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
for (let invalidation of restore()) {
|
|
190
|
-
asset.invalidateOnFileCreate(invalidation);
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
if (!found) {
|
|
194
|
-
throw new Error('failed to locate file ' + originalPath);
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
// Recursively process resolved files as well to get nested deps
|
|
199
|
-
for (let resolved of found) {
|
|
200
|
-
if (!seen.has(resolved)) {
|
|
201
|
-
asset.invalidateOnFileChange(resolved);
|
|
202
|
-
|
|
203
|
-
let code = await asset.fs.readFile(resolved, 'utf8');
|
|
204
|
-
for (let [path, resolvedPath] of await getDependencies(
|
|
205
|
-
code,
|
|
206
|
-
resolved,
|
|
207
|
-
asset,
|
|
208
|
-
resolve,
|
|
209
|
-
options,
|
|
210
|
-
parcelOptions,
|
|
211
|
-
nativeGlob,
|
|
212
|
-
seen,
|
|
213
|
-
false,
|
|
214
|
-
)) {
|
|
215
|
-
res.set(path, resolvedPath);
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
}),
|
|
220
|
-
);
|
|
221
|
-
|
|
222
|
-
return res;
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
async function createEvaluator(
|
|
226
|
-
code,
|
|
227
|
-
asset,
|
|
228
|
-
resolve,
|
|
229
|
-
options,
|
|
230
|
-
parcelOptions,
|
|
231
|
-
nativeGlob,
|
|
232
|
-
) {
|
|
233
|
-
const deps = await getDependencies(
|
|
234
|
-
code,
|
|
235
|
-
asset.filePath,
|
|
236
|
-
asset,
|
|
237
|
-
resolve,
|
|
238
|
-
options,
|
|
239
|
-
parcelOptions,
|
|
240
|
-
nativeGlob,
|
|
241
|
-
);
|
|
242
|
-
|
|
243
|
-
// This is a custom stylus evaluator that extends stylus with support for the node
|
|
244
|
-
// require resolution algorithm. It also adds all dependencies to the parcel asset
|
|
245
|
-
// tree so the file watcher works correctly, etc.
|
|
246
|
-
class CustomEvaluator extends Evaluator {
|
|
247
|
-
visitImport(imported) {
|
|
248
|
-
let node = this.visit(imported.path).first;
|
|
249
|
-
let path = node.string;
|
|
250
|
-
if (node.name !== 'url' && path && !URL_RE.test(path)) {
|
|
251
|
-
let resolved = deps.get(path);
|
|
252
|
-
|
|
253
|
-
// First try resolving using the node require resolution algorithm.
|
|
254
|
-
// This allows stylus files in node_modules to be resolved properly.
|
|
255
|
-
// If we find something, update the AST so stylus gets the absolute path to load later.
|
|
256
|
-
if (resolved) {
|
|
257
|
-
if (!Array.isArray(resolved)) {
|
|
258
|
-
node.string = resolved;
|
|
259
|
-
} else {
|
|
260
|
-
// If the import resolves to multiple files (i.e. glob),
|
|
261
|
-
// replace it with a separate import node for each file
|
|
262
|
-
return mergeBlocks(
|
|
263
|
-
resolved.map(resolvedPath => {
|
|
264
|
-
node.string = resolvedPath;
|
|
265
|
-
let restore = patchNativeFS(asset.fs, nativeGlob);
|
|
266
|
-
let res = super.visitImport(imported.clone());
|
|
267
|
-
restore();
|
|
268
|
-
return res;
|
|
269
|
-
}),
|
|
270
|
-
);
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
// Patch the native FS so stylus uses Parcel's FS to read the file.
|
|
276
|
-
let restore = patchNativeFS(asset.fs, nativeGlob);
|
|
277
|
-
|
|
278
|
-
// Done. Let stylus do its thing.
|
|
279
|
-
let res = super.visitImport(imported);
|
|
280
|
-
|
|
281
|
-
restore();
|
|
282
|
-
return res;
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
return CustomEvaluator;
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
function patchNativeFS(fs, nativeGlob) {
|
|
290
|
-
let invalidations = [];
|
|
291
|
-
let readFileSync = nativeFS.readFileSync;
|
|
292
|
-
let statSync = nativeFS.statSync;
|
|
293
|
-
|
|
294
|
-
// $FlowFixMe
|
|
295
|
-
nativeFS.readFileSync = (filename, encoding) => {
|
|
296
|
-
return fs.readFileSync(filename, encoding);
|
|
297
|
-
};
|
|
298
|
-
|
|
299
|
-
// $FlowFixMe
|
|
300
|
-
nativeFS.statSync = p => {
|
|
301
|
-
try {
|
|
302
|
-
return fs.statSync(p);
|
|
303
|
-
} catch (err) {
|
|
304
|
-
// Track files that were checked but don't exist so that we watch for their creation.
|
|
305
|
-
if (!p.includes(`node_modules${path.sep}stylus`)) {
|
|
306
|
-
invalidations.push({filePath: p});
|
|
307
|
-
}
|
|
308
|
-
throw err;
|
|
309
|
-
}
|
|
310
|
-
};
|
|
311
|
-
|
|
312
|
-
// Patch the `glob` module as well so we use the Parcel FS and track invalidations.
|
|
313
|
-
let glob = nativeGlob.sync;
|
|
314
|
-
nativeGlob.sync = p => {
|
|
315
|
-
let res = globSync(p, fs);
|
|
316
|
-
if (!p.includes(`node_modules${path.sep}stylus`)) {
|
|
317
|
-
// Sometimes stylus passes file paths with no glob parts to the `glob` module.
|
|
318
|
-
// We want to avoid treating these as globs for performance.
|
|
319
|
-
if (isGlob(p)) {
|
|
320
|
-
invalidations.push({glob: p});
|
|
321
|
-
} else if (res.length === 0) {
|
|
322
|
-
invalidations.push({filePath: p});
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
return res;
|
|
326
|
-
};
|
|
327
|
-
|
|
328
|
-
return () => {
|
|
329
|
-
// $FlowFixMe
|
|
330
|
-
nativeFS.readFileSync = readFileSync;
|
|
331
|
-
// $FlowFixMe
|
|
332
|
-
nativeFS.statSync = statSync;
|
|
333
|
-
nativeGlob.sync = glob;
|
|
334
|
-
return invalidations;
|
|
335
|
-
};
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
/**
|
|
339
|
-
* Puts the content of all given node blocks into the first one, essentially merging them.
|
|
340
|
-
*/
|
|
341
|
-
function mergeBlocks(blocks) {
|
|
342
|
-
let finalBlock;
|
|
343
|
-
for (const block of blocks) {
|
|
344
|
-
if (finalBlock) {
|
|
345
|
-
// $FlowFixMe - finalBlock is definitely defined
|
|
346
|
-
block.nodes.forEach(node => finalBlock.push(node));
|
|
347
|
-
} else {
|
|
348
|
-
finalBlock = block;
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
return finalBlock;
|
|
352
|
-
}
|