@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.
@@ -1,330 +1,171 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.default = void 0;
7
- function _plugin() {
8
- const data = require("@parcel/plugin");
9
- _plugin = function () {
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
- var _default = exports.default = new (_plugin().Transformer)({
80
- async loadConfig({
81
- config
82
- }) {
83
- let configFile = await config.getConfig(['.stylusrc', '.stylusrc.js', '.stylusrc.cjs', '.stylusrc.mjs'], {
84
- packageKey: 'stylus'
85
- });
86
- if (configFile) {
87
- // Resolve relative paths from config file
88
- if (configFile.contents.paths) {
89
- configFile.contents.paths = configFile.contents.paths.map(p => _path().default.resolve(_path().default.dirname(configFile.filePath), p));
90
- }
91
- return configFile.contents;
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, asset, resolve, deps) {
128
- if (deps.has(importedPath)) {
129
- return;
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
- let found;
176
- if (resolved && (!Array.isArray(resolved) || resolved.length > 0)) {
177
- found = Array.isArray(resolved) ? resolved : [resolved];
178
- res.set(importedPath, resolved);
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
- // If we couldn't resolve, try the normal stylus resolver.
181
- // We just need to do this to keep track of the dependencies - stylus does the real work.
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 createEvaluator(code, asset, resolve, options, parcelOptions, nativeGlob) {
219
- const deps = await getDependencies(code, asset.filePath, asset, resolve, options, parcelOptions, nativeGlob);
220
-
221
- // This is a custom stylus evaluator that extends stylus with support for the node
222
- // require resolution algorithm. It also adds all dependencies to the parcel asset
223
- // tree so the file watcher works correctly, etc.
224
- class CustomEvaluator extends _evaluator().default {
225
- visitImport(imported) {
226
- let node = this.visit(imported.path).first;
227
- let path = node.string;
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 patchNativeFS(fs, nativeGlob) {
263
- let invalidations = [];
264
- let readFileSync = _fs().default.readFileSync;
265
- let statSync = _fs().default.statSync;
266
-
267
- // $FlowFixMe
268
- _fs().default.readFileSync = (filename, encoding) => {
269
- return fs.readFileSync(filename, encoding);
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
- // Patch the `glob` module as well so we use the Parcel FS and track invalidations.
288
- let glob = nativeGlob.sync;
289
- nativeGlob.sync = p => {
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
- function mergeBlocks(blocks) {
320
- let finalBlock;
321
- for (const block of blocks) {
322
- if (finalBlock) {
323
- // $FlowFixMe - finalBlock is definitely defined
324
- block.nodes.forEach(node => finalBlock.push(node));
325
- } else {
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
- return finalBlock;
330
- }
170
+ return finalBlock;
171
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@parcel/transformer-stylus",
3
- "version": "2.16.4",
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
- "source": "src/StylusTransformer.js",
18
+ "files": [
19
+ "lib"
20
+ ],
21
+ "source": "src/StylusTransformer.ts",
18
22
  "engines": {
19
23
  "node": ">= 16.0.0",
20
- "parcel": "^2.16.4"
24
+ "parcel": "^3.0.0-0"
21
25
  },
22
26
  "dependencies": {
23
- "@parcel/plugin": "2.16.4",
24
- "@parcel/utils": "2.16.4",
27
+ "@parcel/plugin": "^3.0.0-alpha.1",
25
28
  "stylus": "^0.63.0"
26
29
  },
27
- "gitHead": "59484858a1a0bcbb71f74088956bb437a2db6505"
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.
@@ -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
- }