From 7bb7311c7897d30d3caf474155ef9ae575cf5c64 Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz Date: Fri, 3 Aug 2018 02:26:02 +0200 Subject: [PATCH] Fix glob import/require in Stylus assets --- src/assets/StylusAsset.js | 58 +++++++++++++++++-- test/integration/stylus-glob-import/index.js | 5 ++ .../integration/stylus-glob-import/index.styl | 4 ++ .../stylus-glob-import/subdir/bar/bar.styl | 2 + .../stylus-glob-import/subdir/foo/foo.styl | 2 + .../stylus-glob-import/subdir/main.styl | 2 + test/stylus.js | 31 ++++++++++ 7 files changed, 99 insertions(+), 5 deletions(-) create mode 100644 test/integration/stylus-glob-import/index.js create mode 100644 test/integration/stylus-glob-import/index.styl create mode 100644 test/integration/stylus-glob-import/subdir/bar/bar.styl create mode 100644 test/integration/stylus-glob-import/subdir/foo/foo.styl create mode 100644 test/integration/stylus-glob-import/subdir/main.styl diff --git a/src/assets/StylusAsset.js b/src/assets/StylusAsset.js index ebb0b42c788..1eff9568447 100644 --- a/src/assets/StylusAsset.js +++ b/src/assets/StylusAsset.js @@ -3,7 +3,9 @@ const Asset = require('../Asset'); const localRequire = require('../utils/localRequire'); const Resolver = require('../Resolver'); const fs = require('../utils/fs'); -const {dirname} = require('path'); +const {dirname, resolve, relative} = require('path'); +const isGlob = require('is-glob'); +const glob = require('fast-glob'); const URL_RE = /^(?:url\s*\(\s*)?['"]?(?:[#/]|(?:https?:)?\/\/)/i; @@ -80,7 +82,25 @@ async function getDependencies( let path = imported.path.first.string; if (!deps.has(path)) { - deps.set(path, resolver.resolve(path, filepath)); + if (isGlob(path)) { + deps.set( + path, + glob(resolve(dirname(filepath), path), { + onlyFiles: true + }).then(entries => + Promise.all( + entries.map(entry => + resolver.resolve( + './' + relative(dirname(filepath), entry), + filepath + ) + ) + ) + ) + ); + } else { + deps.set(path, resolver.resolve(path, filepath)); + } } } } @@ -92,14 +112,17 @@ async function getDependencies( await Promise.all( Array.from(deps.entries()).map(async ([path, resolved]) => { try { - resolved = (await resolved).path; + resolved = await resolved; + resolved = Array.isArray(resolved) + ? resolved.map(r => r.path) + : resolved.path; } catch (err) { resolved = null; } let found; if (resolved) { - found = [resolved]; + found = Array.isArray(resolved) ? resolved : [resolved]; res.set(path, resolved); } else { // If we couldn't resolve, try the normal stylus resolver. @@ -166,7 +189,18 @@ async function createEvaluator(code, asset, options) { // This allows stylus files in node_modules to be resolved properly. // If we find something, update the AST so stylus gets the absolute path to load later. if (resolved) { - node.string = resolved; + if (!Array.isArray(resolved)) { + node.string = resolved; + } else { + // If the import resolves to multiple files (i.e. glob), + // replace it with a separate import node for each file + return mergeBlocks( + resolved.map(resolvedPath => { + node.string = resolvedPath; + return super.visitImport(imported.clone()); + }) + ); + } } } @@ -178,4 +212,18 @@ async function createEvaluator(code, asset, options) { return CustomEvaluator; } +/** + * Puts the content of all given node blocks into the first one, essentially merging them. + */ +function mergeBlocks(blocks) { + let finalBlock; + for (const block of blocks) { + if (!finalBlock) finalBlock = block; + else { + block.nodes.forEach(node => finalBlock.push(node)); + } + } + return finalBlock; +} + module.exports = StylusAsset; diff --git a/test/integration/stylus-glob-import/index.js b/test/integration/stylus-glob-import/index.js new file mode 100644 index 00000000000..03f300fc306 --- /dev/null +++ b/test/integration/stylus-glob-import/index.js @@ -0,0 +1,5 @@ +require('./index.styl'); + +module.exports = function () { + return 2; +}; diff --git a/test/integration/stylus-glob-import/index.styl b/test/integration/stylus-glob-import/index.styl new file mode 100644 index 00000000000..d56d0ada9fb --- /dev/null +++ b/test/integration/stylus-glob-import/index.styl @@ -0,0 +1,4 @@ +@require 'subdir/**/*' + +.index + color: red diff --git a/test/integration/stylus-glob-import/subdir/bar/bar.styl b/test/integration/stylus-glob-import/subdir/bar/bar.styl new file mode 100644 index 00000000000..331c3a42a96 --- /dev/null +++ b/test/integration/stylus-glob-import/subdir/bar/bar.styl @@ -0,0 +1,2 @@ +.bar + color: green diff --git a/test/integration/stylus-glob-import/subdir/foo/foo.styl b/test/integration/stylus-glob-import/subdir/foo/foo.styl new file mode 100644 index 00000000000..d8919c18ec5 --- /dev/null +++ b/test/integration/stylus-glob-import/subdir/foo/foo.styl @@ -0,0 +1,2 @@ +.foo + color: blue diff --git a/test/integration/stylus-glob-import/subdir/main.styl b/test/integration/stylus-glob-import/subdir/main.styl new file mode 100644 index 00000000000..34359c7d043 --- /dev/null +++ b/test/integration/stylus-glob-import/subdir/main.styl @@ -0,0 +1,2 @@ +.main + color: yellow diff --git a/test/stylus.js b/test/stylus.js index 7c63fea2394..045fb127778 100644 --- a/test/stylus.js +++ b/test/stylus.js @@ -124,4 +124,35 @@ describe('stylus', function() { let css = await fs.readFile(__dirname + '/dist/index.css', 'utf8'); assert(css.includes('._index_g9mqo_1')); }); + + it('should support requiring stylus files with glob dependencies', async function() { + let b = await bundle( + __dirname + '/integration/stylus-glob-import/index.js' + ); + + await assertBundleTree(b, { + name: 'index.js', + assets: ['index.js', 'index.styl'], + childBundles: [ + { + type: 'map' + }, + { + name: 'index.css', + assets: ['index.styl'], + childBundles: [] + } + ] + }); + + let output = await run(b); + assert.equal(typeof output, 'function'); + assert.equal(output(), 2); + + let css = await fs.readFile(__dirname + '/dist/index.css', 'utf8'); + assert(css.includes('.index')); + assert(css.includes('.main')); + assert(css.includes('.foo')); + assert(css.includes('.bar')); + }); });