@parcel/transformer-stylus 2.0.0-nightly.92 → 2.0.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.
@@ -5,23 +5,132 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.default = void 0;
7
7
 
8
- var _plugin = require("@parcel/plugin");
8
+ function _plugin() {
9
+ const data = require("@parcel/plugin");
9
10
 
10
- var _utils = require("@parcel/utils");
11
+ _plugin = function () {
12
+ return data;
13
+ };
14
+
15
+ return data;
16
+ }
17
+
18
+ function _utils() {
19
+ const data = require("@parcel/utils");
20
+
21
+ _utils = function () {
22
+ return data;
23
+ };
24
+
25
+ return data;
26
+ }
27
+
28
+ function _path() {
29
+ const data = _interopRequireDefault(require("path"));
30
+
31
+ _path = function () {
32
+ return data;
33
+ };
34
+
35
+ return data;
36
+ }
37
+
38
+ function _fs() {
39
+ const data = _interopRequireDefault(require("fs"));
11
40
 
12
- var _path = _interopRequireDefault(require("path"));
41
+ _fs = function () {
42
+ return data;
43
+ };
44
+
45
+ return data;
46
+ }
47
+
48
+ function _stylus() {
49
+ const data = _interopRequireDefault(require("stylus"));
50
+
51
+ _stylus = function () {
52
+ return data;
53
+ };
54
+
55
+ return data;
56
+ }
57
+
58
+ function _parser() {
59
+ const data = _interopRequireDefault(require("stylus/lib/parser"));
60
+
61
+ _parser = function () {
62
+ return data;
63
+ };
64
+
65
+ return data;
66
+ }
67
+
68
+ function _depsResolver() {
69
+ const data = _interopRequireDefault(require("stylus/lib/visitor/deps-resolver"));
70
+
71
+ _depsResolver = function () {
72
+ return data;
73
+ };
74
+
75
+ return data;
76
+ }
77
+
78
+ function _nodes() {
79
+ const data = _interopRequireDefault(require("stylus/lib/nodes"));
80
+
81
+ _nodes = function () {
82
+ return data;
83
+ };
84
+
85
+ return data;
86
+ }
87
+
88
+ function _utils2() {
89
+ const data = _interopRequireDefault(require("stylus/lib/utils"));
90
+
91
+ _utils2 = function () {
92
+ return data;
93
+ };
94
+
95
+ return data;
96
+ }
97
+
98
+ function _evaluator() {
99
+ const data = _interopRequireDefault(require("stylus/lib/visitor/evaluator"));
100
+
101
+ _evaluator = function () {
102
+ return data;
103
+ };
104
+
105
+ return data;
106
+ }
13
107
 
14
108
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
15
109
 
16
110
  const URL_RE = /^(?:url\s*\(\s*)?['"]?(?:[#/]|(?:https?:)?\/\/)/i;
17
111
 
18
- var _default = new _plugin.Transformer({
19
- getConfig({
20
- asset
112
+ var _default = new (_plugin().Transformer)({
113
+ async loadConfig({
114
+ config
21
115
  }) {
22
- return asset.getConfig(['.stylusrc', '.stylusrc.js'], {
116
+ let configFile = await config.getConfig(['.stylusrc', '.stylusrc.js'], {
23
117
  packageKey: 'stylus'
24
118
  });
119
+
120
+ if (configFile) {
121
+ let isJavascript = _path().default.extname(configFile.filePath) === '.js';
122
+
123
+ if (isJavascript) {
124
+ config.invalidateOnStartup();
125
+ } // Resolve relative paths from config file
126
+
127
+
128
+ if (configFile.contents.paths) {
129
+ configFile.contents.paths = configFile.contents.paths.map(p => _path().default.resolve(_path().default.dirname(configFile.filePath), p));
130
+ }
131
+
132
+ return configFile.contents;
133
+ }
25
134
  },
26
135
 
27
136
  async transform({
@@ -30,18 +139,27 @@ var _default = new _plugin.Transformer({
30
139
  config,
31
140
  options
32
141
  }) {
33
- // stylus should be installed locally in the module that's being required
34
- let stylus = await options.packageManager.require('stylus', asset.filePath);
142
+ let stylusConfig = config !== null && config !== void 0 ? config : {};
35
143
  let code = await asset.getCode();
36
- let style = stylus(code, config);
144
+ let style = (0, _stylus().default)(code, { ...stylusConfig
145
+ });
37
146
  style.set('filename', asset.filePath);
38
147
  style.set('include css', true); // Setup a handler for the URL function so we add dependencies for linked assets.
39
148
 
40
149
  style.define('url', node => {
41
- let filename = asset.addURLDependency(node.val, node.filename);
42
- return new stylus.nodes.Literal(`url(${JSON.stringify(filename)})`);
150
+ let filename = asset.addURLDependency(node.val, {
151
+ loc: (0, _utils().createDependencyLocation)({
152
+ line: node.lineno,
153
+ column: node.column
154
+ }, node.val)
155
+ });
156
+ return new (_stylus().default.nodes.Literal)(`url(${JSON.stringify(filename)})`);
43
157
  });
44
- style.set('Evaluator', (await createEvaluator(code, asset, resolve, style.options, options)));
158
+ let {
159
+ resolved: stylusPath
160
+ } = await options.packageManager.resolve('stylus', __filename);
161
+ let nativeGlob = await options.packageManager.require('glob', stylusPath);
162
+ style.set('Evaluator', await createEvaluator(code, asset, resolve, style.options, options, nativeGlob));
45
163
  asset.type = 'css';
46
164
  asset.setCode(style.render());
47
165
  asset.meta.hasDependencies = false;
@@ -52,29 +170,48 @@ var _default = new _plugin.Transformer({
52
170
 
53
171
  exports.default = _default;
54
172
 
55
- async function getDependencies(code, filepath, asset, resolve, options, parcelOptions, seen = new Set()) {
173
+ function attemptResolve(importedPath, filepath, asset, resolve, deps) {
174
+ if (deps.has(importedPath)) {
175
+ return;
176
+ }
177
+
178
+ if ((0, _utils().isGlob)(importedPath)) {
179
+ // Invalidate when new files are created that match the glob pattern.
180
+ let absoluteGlob = _path().default.resolve(_path().default.dirname(filepath), importedPath);
181
+
182
+ asset.invalidateOnFileCreate({
183
+ glob: absoluteGlob
184
+ });
185
+ deps.set(importedPath, (0, _utils().glob)(absoluteGlob, asset.fs, {
186
+ onlyFiles: true
187
+ }).then(entries => Promise.all(entries.map(entry => resolve(filepath, './' + _path().default.relative(_path().default.dirname(filepath), entry))))));
188
+ } else {
189
+ deps.set(importedPath, resolve(filepath, importedPath));
190
+ }
191
+ }
192
+
193
+ async function getDependencies(code, filepath, asset, resolve, options, parcelOptions, nativeGlob, seen = new Set(), includeImports = true) {
56
194
  seen.add(filepath);
57
- const [Parser, DepsResolver, nodes, utils] = await Promise.all(['parser', 'visitor/deps-resolver', 'nodes', 'utils'].map(dep => parcelOptions.packageManager.require('stylus/lib/' + dep, filepath)));
58
- nodes.filename = asset.filePath;
59
- let parser = new Parser(code, options);
195
+ _nodes().default.filename = asset.filePath;
196
+ let parser = new (_parser().default)(code, options);
60
197
  let ast = parser.parse();
61
198
  let deps = new Map();
62
- new class extends DepsResolver {
199
+
200
+ if (includeImports && options.imports) {
201
+ for (let importedPath of options.imports) {
202
+ attemptResolve(importedPath, filepath, asset, resolve, deps);
203
+ }
204
+ }
205
+
206
+ class ImportVisitor extends _depsResolver().default {
63
207
  visitImport(imported) {
64
208
  let importedPath = imported.path.first.string;
65
-
66
- if (!deps.has(importedPath)) {
67
- if ((0, _utils.isGlob)(importedPath)) {
68
- deps.set(importedPath, (0, _utils.glob)(_path.default.resolve(_path.default.dirname(filepath), importedPath), parcelOptions.inputFS, {
69
- onlyFiles: true
70
- }).then(entries => Promise.all(entries.map(entry => resolve(filepath, './' + _path.default.relative(_path.default.dirname(filepath), entry))))));
71
- } else {
72
- deps.set(importedPath, resolve(filepath, importedPath));
73
- }
74
- }
209
+ attemptResolve(importedPath, filepath, asset, resolve, deps);
75
210
  }
76
211
 
77
- }(ast, options).visit(ast); // Recursively process depdendencies, and return a map with all resolved paths.
212
+ }
213
+
214
+ new ImportVisitor(ast, options).visit(ast); // Recursively process depdendencies, and return a map with all resolved paths.
78
215
 
79
216
  let res = new Map();
80
217
  await Promise.all(Array.from(deps.entries()).map(async ([importedPath, resolved]) => {
@@ -86,7 +223,7 @@ async function getDependencies(code, filepath, asset, resolve, options, parcelOp
86
223
 
87
224
  let found;
88
225
 
89
- if (resolved) {
226
+ if (resolved && (!Array.isArray(resolved) || resolved.length > 0)) {
90
227
  found = Array.isArray(resolved) ? resolved : [resolved];
91
228
  res.set(importedPath, resolved);
92
229
  } else {
@@ -97,13 +234,20 @@ async function getDependencies(code, filepath, asset, resolve, options, parcelOp
97
234
 
98
235
  if (!/\.styl$/i.test(importedPath)) {
99
236
  importedPath += '.styl';
100
- }
237
+ } // Patch the native FS so we use Parcel's FS, and track files that are
238
+ // checked so we invalidate the cache when they are created.
239
+
101
240
 
102
- let paths = (options.paths || []).concat(_path.default.dirname(filepath || '.'));
103
- found = utils.find(importedPath, paths, filepath);
241
+ let restore = patchNativeFS(asset.fs, nativeGlob);
242
+ let paths = [...new Set((options.paths || []).concat(_path().default.dirname(filepath || '.')))];
243
+ found = _utils2().default.find(importedPath, paths, filepath);
104
244
 
105
245
  if (!found) {
106
- found = utils.lookupIndex(originalPath, paths, filepath);
246
+ found = _utils2().default.lookupIndex(originalPath, paths, filepath);
247
+ }
248
+
249
+ for (let invalidation of restore()) {
250
+ asset.invalidateOnFileCreate(invalidation);
107
251
  }
108
252
 
109
253
  if (!found) {
@@ -114,12 +258,10 @@ async function getDependencies(code, filepath, asset, resolve, options, parcelOp
114
258
 
115
259
  for (let resolved of found) {
116
260
  if (!seen.has(resolved)) {
117
- await asset.addIncludedFile({
118
- filePath: resolved
119
- });
261
+ await asset.invalidateOnFileChange(resolved);
120
262
  let code = await asset.fs.readFile(resolved, 'utf8');
121
263
 
122
- for (let [path, resolvedPath] of await getDependencies(code, resolved, asset, resolve, options, parcelOptions, seen)) {
264
+ for (let [path, resolvedPath] of await getDependencies(code, resolved, asset, resolve, options, parcelOptions, nativeGlob, seen, false)) {
123
265
  res.set(path, resolvedPath);
124
266
  }
125
267
  }
@@ -128,13 +270,12 @@ async function getDependencies(code, filepath, asset, resolve, options, parcelOp
128
270
  return res;
129
271
  }
130
272
 
131
- async function createEvaluator(code, asset, resolve, options, parcelOptions) {
132
- const deps = await getDependencies(code, asset.filePath, asset, resolve, options, parcelOptions);
133
- const Evaluator = await parcelOptions.packageManager.require('stylus/lib/visitor/evaluator', asset.filePath); // This is a custom stylus evaluator that extends stylus with support for the node
273
+ async function createEvaluator(code, asset, resolve, options, parcelOptions, nativeGlob) {
274
+ const deps = await getDependencies(code, asset.filePath, asset, resolve, options, parcelOptions, nativeGlob); // This is a custom stylus evaluator that extends stylus with support for the node
134
275
  // require resolution algorithm. It also adds all dependencies to the parcel asset
135
276
  // tree so the file watcher works correctly, etc.
136
277
 
137
- return class extends Evaluator {
278
+ class CustomEvaluator extends _evaluator().default {
138
279
  visitImport(imported) {
139
280
  let node = this.visit(imported.path).first;
140
281
  let path = node.string;
@@ -152,16 +293,86 @@ async function createEvaluator(code, asset, resolve, options, parcelOptions) {
152
293
  // replace it with a separate import node for each file
153
294
  return mergeBlocks(resolved.map(resolvedPath => {
154
295
  node.string = resolvedPath;
155
- return super.visitImport(imported.clone());
296
+ let restore = patchNativeFS(asset.fs, nativeGlob);
297
+ let res = super.visitImport(imported.clone());
298
+ restore();
299
+ return res;
156
300
  }));
157
301
  }
158
302
  }
159
- } // Done. Let stylus do its thing.
303
+ } // Patch the native FS so stylus uses Parcel's FS to read the file.
304
+
305
+
306
+ let restore = patchNativeFS(asset.fs, nativeGlob); // Done. Let stylus do its thing.
307
+
308
+ let res = super.visitImport(imported);
309
+ restore();
310
+ return res;
311
+ }
312
+
313
+ }
314
+
315
+ return CustomEvaluator;
316
+ }
317
+
318
+ function patchNativeFS(fs, nativeGlob) {
319
+ let invalidations = [];
320
+
321
+ let readFileSync = _fs().default.readFileSync;
322
+
323
+ let statSync = _fs().default.statSync; // $FlowFixMe
324
+
325
+
326
+ _fs().default.readFileSync = (filename, encoding) => {
327
+ return fs.readFileSync(filename, encoding);
328
+ }; // $FlowFixMe
329
+
330
+
331
+ _fs().default.statSync = p => {
332
+ try {
333
+ return fs.statSync(p);
334
+ } catch (err) {
335
+ // Track files that were checked but don't exist so that we watch for their creation.
336
+ if (!p.includes(`node_modules${_path().default.sep}stylus`)) {
337
+ invalidations.push({
338
+ filePath: p
339
+ });
340
+ }
341
+
342
+ throw err;
343
+ }
344
+ }; // Patch the `glob` module as well so we use the Parcel FS and track invalidations.
345
+
346
+
347
+ let glob = nativeGlob.sync;
160
348
 
349
+ nativeGlob.sync = p => {
350
+ let res = (0, _utils().globSync)(p, fs);
161
351
 
162
- return super.visitImport(imported);
352
+ if (!p.includes(`node_modules${_path().default.sep}stylus`)) {
353
+ // Sometimes stylus passes file paths with no glob parts to the `glob` module.
354
+ // We want to avoid treating these as globs for performance.
355
+ if ((0, _utils().isGlob)(p)) {
356
+ invalidations.push({
357
+ glob: p
358
+ });
359
+ } else if (res.length === 0) {
360
+ invalidations.push({
361
+ filePath: p
362
+ });
363
+ }
163
364
  }
164
365
 
366
+ return res;
367
+ };
368
+
369
+ return () => {
370
+ // $FlowFixMe
371
+ _fs().default.readFileSync = readFileSync; // $FlowFixMe
372
+
373
+ _fs().default.statSync = statSync;
374
+ nativeGlob.sync = glob;
375
+ return invalidations;
165
376
  };
166
377
  }
167
378
  /**
package/package.json CHANGED
@@ -1,10 +1,14 @@
1
1
  {
2
2
  "name": "@parcel/transformer-stylus",
3
- "version": "2.0.0-nightly.92+c0655c56",
3
+ "version": "2.0.1",
4
4
  "license": "MIT",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
8
+ "funding": {
9
+ "type": "opencollective",
10
+ "url": "https://opencollective.com/parcel"
11
+ },
8
12
  "repository": {
9
13
  "type": "git",
10
14
  "url": "https://github.com/parcel-bundler/parcel.git"
@@ -12,15 +16,13 @@
12
16
  "main": "lib/StylusTransformer.js",
13
17
  "source": "src/StylusTransformer.js",
14
18
  "engines": {
15
- "node": ">= 10.0.0",
16
- "parcel": "^2.0.0-alpha.1.1"
19
+ "node": ">= 12.0.0",
20
+ "parcel": "^2.0.1"
17
21
  },
18
22
  "dependencies": {
19
- "@parcel/plugin": "2.0.0-nightly.92+c0655c56",
20
- "@parcel/utils": "2.0.0-nightly.92+c0655c56"
21
- },
22
- "devDependencies": {
23
- "stylus": "^0.54.5"
23
+ "@parcel/plugin": "^2.0.1",
24
+ "@parcel/utils": "^2.0.1",
25
+ "stylus": "^0.55.0"
24
26
  },
25
- "gitHead": "c0655c56f7973492fdb28671029ddd923f17a244"
27
+ "gitHead": "28b47e6bdca7de2a06b7cc39a4a0b1df89f3fe15"
26
28
  }
@@ -1,34 +1,74 @@
1
1
  // @flow
2
2
 
3
3
  import {Transformer} from '@parcel/plugin';
4
- import {isGlob, glob} from '@parcel/utils';
4
+ import {createDependencyLocation, isGlob, glob, globSync} from '@parcel/utils';
5
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';
6
13
 
7
14
  const URL_RE = /^(?:url\s*\(\s*)?['"]?(?:[#/]|(?:https?:)?\/\/)/i;
8
15
 
9
- export default new Transformer({
10
- getConfig({asset}) {
11
- return asset.getConfig(['.stylusrc', '.stylusrc.js'], {
16
+ export default (new Transformer({
17
+ async loadConfig({config}) {
18
+ let configFile = await config.getConfig(['.stylusrc', '.stylusrc.js'], {
12
19
  packageKey: 'stylus',
13
20
  });
21
+
22
+ if (configFile) {
23
+ let isJavascript = path.extname(configFile.filePath) === '.js';
24
+ if (isJavascript) {
25
+ config.invalidateOnStartup();
26
+ }
27
+
28
+ // Resolve relative paths from config file
29
+ if (configFile.contents.paths) {
30
+ configFile.contents.paths = configFile.contents.paths.map(p =>
31
+ path.resolve(path.dirname(configFile.filePath), p),
32
+ );
33
+ }
34
+
35
+ return configFile.contents;
36
+ }
14
37
  },
15
38
 
16
39
  async transform({asset, resolve, config, options}) {
17
- // stylus should be installed locally in the module that's being required
18
- let stylus = await options.packageManager.require('stylus', asset.filePath);
19
-
40
+ let stylusConfig = config ?? {};
20
41
  let code = await asset.getCode();
21
- let style = stylus(code, config);
42
+ let style = stylus(code, {...stylusConfig});
22
43
  style.set('filename', asset.filePath);
23
44
  style.set('include css', true);
24
45
  // Setup a handler for the URL function so we add dependencies for linked assets.
25
- style.define('url', node => {
26
- let filename = asset.addURLDependency(node.val, node.filename);
46
+ style.define('url', (node: stylus.nodes.String | stylus.nodes.Literal) => {
47
+ let filename = asset.addURLDependency(node.val, {
48
+ loc: createDependencyLocation(
49
+ {line: node.lineno, column: node.column},
50
+ node.val,
51
+ ),
52
+ });
27
53
  return new stylus.nodes.Literal(`url(${JSON.stringify(filename)})`);
28
54
  });
55
+
56
+ let {resolved: stylusPath} = await options.packageManager.resolve(
57
+ 'stylus',
58
+ __filename,
59
+ );
60
+ let nativeGlob = await options.packageManager.require('glob', stylusPath);
61
+
29
62
  style.set(
30
63
  'Evaluator',
31
- await createEvaluator(code, asset, resolve, style.options, options),
64
+ await createEvaluator(
65
+ code,
66
+ asset,
67
+ resolve,
68
+ style.options,
69
+ options,
70
+ nativeGlob,
71
+ ),
32
72
  );
33
73
 
34
74
  asset.type = 'css';
@@ -36,7 +76,37 @@ export default new Transformer({
36
76
  asset.meta.hasDependencies = false;
37
77
  return [asset];
38
78
  },
39
- });
79
+ }): Transformer);
80
+
81
+ function attemptResolve(importedPath, filepath, asset, resolve, deps) {
82
+ if (deps.has(importedPath)) {
83
+ return;
84
+ }
85
+
86
+ if (isGlob(importedPath)) {
87
+ // Invalidate when new files are created that match the glob pattern.
88
+ let absoluteGlob = path.resolve(path.dirname(filepath), importedPath);
89
+ asset.invalidateOnFileCreate({glob: absoluteGlob});
90
+
91
+ deps.set(
92
+ importedPath,
93
+ glob(absoluteGlob, asset.fs, {
94
+ onlyFiles: true,
95
+ }).then(entries =>
96
+ Promise.all(
97
+ entries.map(entry =>
98
+ resolve(
99
+ filepath,
100
+ './' + path.relative(path.dirname(filepath), entry),
101
+ ),
102
+ ),
103
+ ),
104
+ ),
105
+ );
106
+ } else {
107
+ deps.set(importedPath, resolve(filepath, importedPath));
108
+ }
109
+ }
40
110
 
41
111
  async function getDependencies(
42
112
  code,
@@ -45,14 +115,11 @@ async function getDependencies(
45
115
  resolve,
46
116
  options,
47
117
  parcelOptions,
118
+ nativeGlob,
48
119
  seen = new Set(),
120
+ includeImports = true,
49
121
  ) {
50
122
  seen.add(filepath);
51
- const [Parser, DepsResolver, nodes, utils] = await Promise.all(
52
- ['parser', 'visitor/deps-resolver', 'nodes', 'utils'].map(dep =>
53
- parcelOptions.packageManager.require('stylus/lib/' + dep, filepath),
54
- ),
55
- );
56
123
 
57
124
  nodes.filename = asset.filePath;
58
125
 
@@ -60,35 +127,16 @@ async function getDependencies(
60
127
  let ast = parser.parse();
61
128
  let deps = new Map();
62
129
 
130
+ if (includeImports && options.imports) {
131
+ for (let importedPath of options.imports) {
132
+ attemptResolve(importedPath, filepath, asset, resolve, deps);
133
+ }
134
+ }
135
+
63
136
  class ImportVisitor extends DepsResolver {
64
137
  visitImport(imported) {
65
138
  let importedPath = imported.path.first.string;
66
-
67
- if (!deps.has(importedPath)) {
68
- if (isGlob(importedPath)) {
69
- deps.set(
70
- importedPath,
71
- glob(
72
- path.resolve(path.dirname(filepath), importedPath),
73
- parcelOptions.inputFS,
74
- {
75
- onlyFiles: true,
76
- },
77
- ).then(entries =>
78
- Promise.all(
79
- entries.map(entry =>
80
- resolve(
81
- filepath,
82
- './' + path.relative(path.dirname(filepath), entry),
83
- ),
84
- ),
85
- ),
86
- ),
87
- );
88
- } else {
89
- deps.set(importedPath, resolve(filepath, importedPath));
90
- }
91
- }
139
+ attemptResolve(importedPath, filepath, asset, resolve, deps);
92
140
  }
93
141
  }
94
142
 
@@ -105,7 +153,7 @@ async function getDependencies(
105
153
  }
106
154
 
107
155
  let found;
108
- if (resolved) {
156
+ if (resolved && (!Array.isArray(resolved) || resolved.length > 0)) {
109
157
  found = Array.isArray(resolved) ? resolved : [resolved];
110
158
  res.set(importedPath, resolved);
111
159
  } else {
@@ -118,12 +166,24 @@ async function getDependencies(
118
166
  importedPath += '.styl';
119
167
  }
120
168
 
121
- let paths = (options.paths || []).concat(path.dirname(filepath || '.'));
169
+ // Patch the native FS so we use Parcel's FS, and track files that are
170
+ // checked so we invalidate the cache when they are created.
171
+ let restore = patchNativeFS(asset.fs, nativeGlob);
172
+
173
+ let paths = [
174
+ ...new Set(
175
+ (options.paths || []).concat(path.dirname(filepath || '.')),
176
+ ),
177
+ ];
122
178
  found = utils.find(importedPath, paths, filepath);
123
179
  if (!found) {
124
180
  found = utils.lookupIndex(originalPath, paths, filepath);
125
181
  }
126
182
 
183
+ for (let invalidation of restore()) {
184
+ asset.invalidateOnFileCreate(invalidation);
185
+ }
186
+
127
187
  if (!found) {
128
188
  throw new Error('failed to locate file ' + originalPath);
129
189
  }
@@ -132,7 +192,7 @@ async function getDependencies(
132
192
  // Recursively process resolved files as well to get nested deps
133
193
  for (let resolved of found) {
134
194
  if (!seen.has(resolved)) {
135
- await asset.addIncludedFile({filePath: resolved});
195
+ await asset.invalidateOnFileChange(resolved);
136
196
 
137
197
  let code = await asset.fs.readFile(resolved, 'utf8');
138
198
  for (let [path, resolvedPath] of await getDependencies(
@@ -142,7 +202,9 @@ async function getDependencies(
142
202
  resolve,
143
203
  options,
144
204
  parcelOptions,
205
+ nativeGlob,
145
206
  seen,
207
+ false,
146
208
  )) {
147
209
  res.set(path, resolvedPath);
148
210
  }
@@ -154,7 +216,14 @@ async function getDependencies(
154
216
  return res;
155
217
  }
156
218
 
157
- async function createEvaluator(code, asset, resolve, options, parcelOptions) {
219
+ async function createEvaluator(
220
+ code,
221
+ asset,
222
+ resolve,
223
+ options,
224
+ parcelOptions,
225
+ nativeGlob,
226
+ ) {
158
227
  const deps = await getDependencies(
159
228
  code,
160
229
  asset.filePath,
@@ -162,10 +231,7 @@ async function createEvaluator(code, asset, resolve, options, parcelOptions) {
162
231
  resolve,
163
232
  options,
164
233
  parcelOptions,
165
- );
166
- const Evaluator = await parcelOptions.packageManager.require(
167
- 'stylus/lib/visitor/evaluator',
168
- asset.filePath,
234
+ nativeGlob,
169
235
  );
170
236
 
171
237
  // This is a custom stylus evaluator that extends stylus with support for the node
@@ -190,21 +256,79 @@ async function createEvaluator(code, asset, resolve, options, parcelOptions) {
190
256
  return mergeBlocks(
191
257
  resolved.map(resolvedPath => {
192
258
  node.string = resolvedPath;
193
- return super.visitImport(imported.clone());
259
+ let restore = patchNativeFS(asset.fs, nativeGlob);
260
+ let res = super.visitImport(imported.clone());
261
+ restore();
262
+ return res;
194
263
  }),
195
264
  );
196
265
  }
197
266
  }
198
267
  }
199
268
 
269
+ // Patch the native FS so stylus uses Parcel's FS to read the file.
270
+ let restore = patchNativeFS(asset.fs, nativeGlob);
271
+
200
272
  // Done. Let stylus do its thing.
201
- return super.visitImport(imported);
273
+ let res = super.visitImport(imported);
274
+
275
+ restore();
276
+ return res;
202
277
  }
203
278
  }
204
279
 
205
280
  return CustomEvaluator;
206
281
  }
207
282
 
283
+ function patchNativeFS(fs, nativeGlob) {
284
+ let invalidations = [];
285
+ let readFileSync = nativeFS.readFileSync;
286
+ let statSync = nativeFS.statSync;
287
+
288
+ // $FlowFixMe
289
+ nativeFS.readFileSync = (filename, encoding) => {
290
+ return fs.readFileSync(filename, encoding);
291
+ };
292
+
293
+ // $FlowFixMe
294
+ nativeFS.statSync = p => {
295
+ try {
296
+ return fs.statSync(p);
297
+ } catch (err) {
298
+ // Track files that were checked but don't exist so that we watch for their creation.
299
+ if (!p.includes(`node_modules${path.sep}stylus`)) {
300
+ invalidations.push({filePath: p});
301
+ }
302
+ throw err;
303
+ }
304
+ };
305
+
306
+ // Patch the `glob` module as well so we use the Parcel FS and track invalidations.
307
+ let glob = nativeGlob.sync;
308
+ nativeGlob.sync = p => {
309
+ let res = globSync(p, fs);
310
+ if (!p.includes(`node_modules${path.sep}stylus`)) {
311
+ // Sometimes stylus passes file paths with no glob parts to the `glob` module.
312
+ // We want to avoid treating these as globs for performance.
313
+ if (isGlob(p)) {
314
+ invalidations.push({glob: p});
315
+ } else if (res.length === 0) {
316
+ invalidations.push({filePath: p});
317
+ }
318
+ }
319
+ return res;
320
+ };
321
+
322
+ return () => {
323
+ // $FlowFixMe
324
+ nativeFS.readFileSync = readFileSync;
325
+ // $FlowFixMe
326
+ nativeFS.statSync = statSync;
327
+ nativeGlob.sync = glob;
328
+ return invalidations;
329
+ };
330
+ }
331
+
208
332
  /**
209
333
  * Puts the content of all given node blocks into the first one, essentially merging them.
210
334
  */