@parcel/transformer-stylus 2.0.0-beta.1 → 2.0.0-nightly.1002

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,17 +5,111 @@ 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"));
40
+
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"));
11
80
 
12
- var _path = _interopRequireDefault(require("path"));
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({
112
+ var _default = new (_plugin().Transformer)({
19
113
  async loadConfig({
20
114
  config
21
115
  }) {
@@ -24,27 +118,18 @@ var _default = new _plugin.Transformer({
24
118
  });
25
119
 
26
120
  if (configFile) {
27
- let isJavascript = _path.default.extname(configFile.filePath) === '.js';
121
+ let isJavascript = _path().default.extname(configFile.filePath) === '.js';
28
122
 
29
123
  if (isJavascript) {
30
- config.shouldInvalidateOnStartup();
31
- config.shouldReload();
32
- }
124
+ config.invalidateOnStartup();
125
+ } // Resolve relative paths from config file
33
126
 
34
- config.setResult({
35
- contents: configFile.contents,
36
- isSerialisable: !isJavascript
37
- });
38
- }
39
- },
40
127
 
41
- preSerializeConfig({
42
- config
43
- }) {
44
- if (!config.result) return; // Ensure we dont try to serialise functions
128
+ if (configFile.contents.paths) {
129
+ configFile.contents.paths = configFile.contents.paths.map(p => _path().default.resolve(_path().default.dirname(configFile.filePath), p));
130
+ }
45
131
 
46
- if (!config.result.isSerialisable) {
47
- config.result.contents = {};
132
+ return configFile.contents;
48
133
  }
49
134
  },
50
135
 
@@ -54,21 +139,27 @@ var _default = new _plugin.Transformer({
54
139
  config,
55
140
  options
56
141
  }) {
57
- let stylusConfig = config ? config.contents : {}; // stylus should be installed locally in the module that's being required
58
-
59
- let stylus = await options.packageManager.require('stylus', asset.filePath, {
60
- autoinstall: options.autoinstall
61
- });
142
+ let stylusConfig = config !== null && config !== void 0 ? config : {};
62
143
  let code = await asset.getCode();
63
- let style = stylus(code, stylusConfig);
144
+ let style = (0, _stylus().default)(code, { ...stylusConfig
145
+ });
64
146
  style.set('filename', asset.filePath);
65
147
  style.set('include css', true); // Setup a handler for the URL function so we add dependencies for linked assets.
66
148
 
67
149
  style.define('url', node => {
68
- let filename = asset.addURLDependency(node.val, node.filename);
69
- 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)})`);
70
157
  });
71
- 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));
72
163
  asset.type = 'css';
73
164
  asset.setCode(style.render());
74
165
  asset.meta.hasDependencies = false;
@@ -79,31 +170,48 @@ var _default = new _plugin.Transformer({
79
170
 
80
171
  exports.default = _default;
81
172
 
82
- 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) {
83
194
  seen.add(filepath);
84
- const [Parser, DepsResolver, nodes, utils] = await Promise.all(['parser', 'visitor/deps-resolver', 'nodes', 'utils'].map(dep => parcelOptions.packageManager.require('stylus/lib/' + dep, filepath, {
85
- autoinstall: options.autoinstall
86
- })));
87
- nodes.filename = asset.filePath;
88
- let parser = new Parser(code, options);
195
+ _nodes().default.filename = asset.filePath;
196
+ let parser = new (_parser().default)(code, options);
89
197
  let ast = parser.parse();
90
198
  let deps = new Map();
91
- new class ImportVisitor 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 {
92
207
  visitImport(imported) {
93
208
  let importedPath = imported.path.first.string;
94
-
95
- if (!deps.has(importedPath)) {
96
- if ((0, _utils.isGlob)(importedPath)) {
97
- deps.set(importedPath, (0, _utils.glob)(_path.default.resolve(_path.default.dirname(filepath), importedPath), parcelOptions.inputFS, {
98
- onlyFiles: true
99
- }).then(entries => Promise.all(entries.map(entry => resolve(filepath, './' + _path.default.relative(_path.default.dirname(filepath), entry))))));
100
- } else {
101
- deps.set(importedPath, resolve(filepath, importedPath));
102
- }
103
- }
209
+ attemptResolve(importedPath, filepath, asset, resolve, deps);
104
210
  }
105
211
 
106
- }(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.
107
215
 
108
216
  let res = new Map();
109
217
  await Promise.all(Array.from(deps.entries()).map(async ([importedPath, resolved]) => {
@@ -115,7 +223,7 @@ async function getDependencies(code, filepath, asset, resolve, options, parcelOp
115
223
 
116
224
  let found;
117
225
 
118
- if (resolved) {
226
+ if (resolved && (!Array.isArray(resolved) || resolved.length > 0)) {
119
227
  found = Array.isArray(resolved) ? resolved : [resolved];
120
228
  res.set(importedPath, resolved);
121
229
  } else {
@@ -126,13 +234,20 @@ async function getDependencies(code, filepath, asset, resolve, options, parcelOp
126
234
 
127
235
  if (!/\.styl$/i.test(importedPath)) {
128
236
  importedPath += '.styl';
129
- }
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.
130
239
 
131
- let paths = (options.paths || []).concat(_path.default.dirname(filepath || '.'));
132
- found = utils.find(importedPath, paths, filepath);
240
+
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);
133
244
 
134
245
  if (!found) {
135
- 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);
136
251
  }
137
252
 
138
253
  if (!found) {
@@ -143,12 +258,10 @@ async function getDependencies(code, filepath, asset, resolve, options, parcelOp
143
258
 
144
259
  for (let resolved of found) {
145
260
  if (!seen.has(resolved)) {
146
- await asset.addIncludedFile({
147
- filePath: resolved
148
- });
261
+ await asset.invalidateOnFileChange(resolved);
149
262
  let code = await asset.fs.readFile(resolved, 'utf8');
150
263
 
151
- 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)) {
152
265
  res.set(path, resolvedPath);
153
266
  }
154
267
  }
@@ -157,13 +270,12 @@ async function getDependencies(code, filepath, asset, resolve, options, parcelOp
157
270
  return res;
158
271
  }
159
272
 
160
- async function createEvaluator(code, asset, resolve, options, parcelOptions) {
161
- const deps = await getDependencies(code, asset.filePath, asset, resolve, options, parcelOptions);
162
- 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
163
275
  // require resolution algorithm. It also adds all dependencies to the parcel asset
164
276
  // tree so the file watcher works correctly, etc.
165
277
 
166
- return class CustomEvaluator extends Evaluator {
278
+ class CustomEvaluator extends _evaluator().default {
167
279
  visitImport(imported) {
168
280
  let node = this.visit(imported.path).first;
169
281
  let path = node.string;
@@ -181,16 +293,86 @@ async function createEvaluator(code, asset, resolve, options, parcelOptions) {
181
293
  // replace it with a separate import node for each file
182
294
  return mergeBlocks(resolved.map(resolvedPath => {
183
295
  node.string = resolvedPath;
184
- return super.visitImport(imported.clone());
296
+ let restore = patchNativeFS(asset.fs, nativeGlob);
297
+ let res = super.visitImport(imported.clone());
298
+ restore();
299
+ return res;
185
300
  }));
186
301
  }
187
302
  }
188
- } // 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.
189
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;
190
322
 
191
- return super.visitImport(imported);
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;
192
343
  }
344
+ }; // Patch the `glob` module as well so we use the Parcel FS and track invalidations.
345
+
346
+
347
+ let glob = nativeGlob.sync;
348
+
349
+ nativeGlob.sync = p => {
350
+ let res = (0, _utils().globSync)(p, fs);
351
+
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
+ }
364
+ }
365
+
366
+ return res;
367
+ };
368
+
369
+ return () => {
370
+ // $FlowFixMe
371
+ _fs().default.readFileSync = readFileSync; // $FlowFixMe
193
372
 
373
+ _fs().default.statSync = statSync;
374
+ nativeGlob.sync = glob;
375
+ return invalidations;
194
376
  };
195
377
  }
196
378
  /**
package/package.json CHANGED
@@ -1,10 +1,14 @@
1
1
  {
2
2
  "name": "@parcel/transformer-stylus",
3
- "version": "2.0.0-beta.1",
3
+ "version": "2.0.0-nightly.1002+5530a6ef",
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.0-nightly.1000+5530a6ef"
17
21
  },
18
22
  "dependencies": {
19
- "@parcel/plugin": "2.0.0-beta.1",
20
- "@parcel/utils": "2.0.0-beta.1"
21
- },
22
- "devDependencies": {
23
- "stylus": "^0.54.5"
23
+ "@parcel/plugin": "2.0.0-nightly.1002+5530a6ef",
24
+ "@parcel/utils": "2.0.0-nightly.1002+5530a6ef",
25
+ "stylus": "^0.55.0"
24
26
  },
25
- "gitHead": "74335525be92e23bac4ed1bf30595443cfb238e3"
27
+ "gitHead": "5530a6eff8b619873353baeb0457ae4ec591e9fa"
26
28
  }
@@ -1,12 +1,19 @@
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({
16
+ export default (new Transformer({
10
17
  async loadConfig({config}) {
11
18
  let configFile = await config.getConfig(['.stylusrc', '.stylusrc.js'], {
12
19
  packageKey: 'stylus',
@@ -15,47 +22,53 @@ export default new Transformer({
15
22
  if (configFile) {
16
23
  let isJavascript = path.extname(configFile.filePath) === '.js';
17
24
  if (isJavascript) {
18
- config.shouldInvalidateOnStartup();
19
- config.shouldReload();
25
+ config.invalidateOnStartup();
20
26
  }
21
27
 
22
- config.setResult({
23
- contents: configFile.contents,
24
- isSerialisable: !isJavascript,
25
- });
26
- }
27
- },
28
-
29
- preSerializeConfig({config}) {
30
- if (!config.result) return;
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
+ }
31
34
 
32
- // Ensure we dont try to serialise functions
33
- if (!config.result.isSerialisable) {
34
- config.result.contents = {};
35
+ return configFile.contents;
35
36
  }
36
37
  },
37
38
 
38
39
  async transform({asset, resolve, config, options}) {
39
- let stylusConfig = config ? config.contents : {};
40
- // stylus should be installed locally in the module that's being required
41
- let stylus = await options.packageManager.require(
42
- 'stylus',
43
- asset.filePath,
44
- {autoinstall: options.autoinstall},
45
- );
46
-
40
+ let stylusConfig = config ?? {};
47
41
  let code = await asset.getCode();
48
- let style = stylus(code, stylusConfig);
42
+ let style = stylus(code, {...stylusConfig});
49
43
  style.set('filename', asset.filePath);
50
44
  style.set('include css', true);
51
45
  // Setup a handler for the URL function so we add dependencies for linked assets.
52
- style.define('url', node => {
53
- 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
+ });
54
53
  return new stylus.nodes.Literal(`url(${JSON.stringify(filename)})`);
55
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
+
56
62
  style.set(
57
63
  'Evaluator',
58
- 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
+ ),
59
72
  );
60
73
 
61
74
  asset.type = 'css';
@@ -63,7 +76,37 @@ export default new Transformer({
63
76
  asset.meta.hasDependencies = false;
64
77
  return [asset];
65
78
  },
66
- });
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
+ }
67
110
 
68
111
  async function getDependencies(
69
112
  code,
@@ -72,16 +115,11 @@ async function getDependencies(
72
115
  resolve,
73
116
  options,
74
117
  parcelOptions,
118
+ nativeGlob,
75
119
  seen = new Set(),
120
+ includeImports = true,
76
121
  ) {
77
122
  seen.add(filepath);
78
- const [Parser, DepsResolver, nodes, utils] = await Promise.all(
79
- ['parser', 'visitor/deps-resolver', 'nodes', 'utils'].map(dep =>
80
- parcelOptions.packageManager.require('stylus/lib/' + dep, filepath, {
81
- autoinstall: options.autoinstall,
82
- }),
83
- ),
84
- );
85
123
 
86
124
  nodes.filename = asset.filePath;
87
125
 
@@ -89,35 +127,16 @@ async function getDependencies(
89
127
  let ast = parser.parse();
90
128
  let deps = new Map();
91
129
 
130
+ if (includeImports && options.imports) {
131
+ for (let importedPath of options.imports) {
132
+ attemptResolve(importedPath, filepath, asset, resolve, deps);
133
+ }
134
+ }
135
+
92
136
  class ImportVisitor extends DepsResolver {
93
137
  visitImport(imported) {
94
138
  let importedPath = imported.path.first.string;
95
-
96
- if (!deps.has(importedPath)) {
97
- if (isGlob(importedPath)) {
98
- deps.set(
99
- importedPath,
100
- glob(
101
- path.resolve(path.dirname(filepath), importedPath),
102
- parcelOptions.inputFS,
103
- {
104
- onlyFiles: true,
105
- },
106
- ).then(entries =>
107
- Promise.all(
108
- entries.map(entry =>
109
- resolve(
110
- filepath,
111
- './' + path.relative(path.dirname(filepath), entry),
112
- ),
113
- ),
114
- ),
115
- ),
116
- );
117
- } else {
118
- deps.set(importedPath, resolve(filepath, importedPath));
119
- }
120
- }
139
+ attemptResolve(importedPath, filepath, asset, resolve, deps);
121
140
  }
122
141
  }
123
142
 
@@ -134,7 +153,7 @@ async function getDependencies(
134
153
  }
135
154
 
136
155
  let found;
137
- if (resolved) {
156
+ if (resolved && (!Array.isArray(resolved) || resolved.length > 0)) {
138
157
  found = Array.isArray(resolved) ? resolved : [resolved];
139
158
  res.set(importedPath, resolved);
140
159
  } else {
@@ -147,12 +166,24 @@ async function getDependencies(
147
166
  importedPath += '.styl';
148
167
  }
149
168
 
150
- 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
+ ];
151
178
  found = utils.find(importedPath, paths, filepath);
152
179
  if (!found) {
153
180
  found = utils.lookupIndex(originalPath, paths, filepath);
154
181
  }
155
182
 
183
+ for (let invalidation of restore()) {
184
+ asset.invalidateOnFileCreate(invalidation);
185
+ }
186
+
156
187
  if (!found) {
157
188
  throw new Error('failed to locate file ' + originalPath);
158
189
  }
@@ -161,7 +192,7 @@ async function getDependencies(
161
192
  // Recursively process resolved files as well to get nested deps
162
193
  for (let resolved of found) {
163
194
  if (!seen.has(resolved)) {
164
- await asset.addIncludedFile({filePath: resolved});
195
+ await asset.invalidateOnFileChange(resolved);
165
196
 
166
197
  let code = await asset.fs.readFile(resolved, 'utf8');
167
198
  for (let [path, resolvedPath] of await getDependencies(
@@ -171,7 +202,9 @@ async function getDependencies(
171
202
  resolve,
172
203
  options,
173
204
  parcelOptions,
205
+ nativeGlob,
174
206
  seen,
207
+ false,
175
208
  )) {
176
209
  res.set(path, resolvedPath);
177
210
  }
@@ -183,7 +216,14 @@ async function getDependencies(
183
216
  return res;
184
217
  }
185
218
 
186
- 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
+ ) {
187
227
  const deps = await getDependencies(
188
228
  code,
189
229
  asset.filePath,
@@ -191,10 +231,7 @@ async function createEvaluator(code, asset, resolve, options, parcelOptions) {
191
231
  resolve,
192
232
  options,
193
233
  parcelOptions,
194
- );
195
- const Evaluator = await parcelOptions.packageManager.require(
196
- 'stylus/lib/visitor/evaluator',
197
- asset.filePath,
234
+ nativeGlob,
198
235
  );
199
236
 
200
237
  // This is a custom stylus evaluator that extends stylus with support for the node
@@ -219,21 +256,79 @@ async function createEvaluator(code, asset, resolve, options, parcelOptions) {
219
256
  return mergeBlocks(
220
257
  resolved.map(resolvedPath => {
221
258
  node.string = resolvedPath;
222
- return super.visitImport(imported.clone());
259
+ let restore = patchNativeFS(asset.fs, nativeGlob);
260
+ let res = super.visitImport(imported.clone());
261
+ restore();
262
+ return res;
223
263
  }),
224
264
  );
225
265
  }
226
266
  }
227
267
  }
228
268
 
269
+ // Patch the native FS so stylus uses Parcel's FS to read the file.
270
+ let restore = patchNativeFS(asset.fs, nativeGlob);
271
+
229
272
  // Done. Let stylus do its thing.
230
- return super.visitImport(imported);
273
+ let res = super.visitImport(imported);
274
+
275
+ restore();
276
+ return res;
231
277
  }
232
278
  }
233
279
 
234
280
  return CustomEvaluator;
235
281
  }
236
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
+
237
332
  /**
238
333
  * Puts the content of all given node blocks into the first one, essentially merging them.
239
334
  */