Skip to content

Commit

Permalink
feat: webpack resolver supports the import option
Browse files Browse the repository at this point in the history
  • Loading branch information
cap-Bernardito committed Oct 2, 2020
1 parent 1be8169 commit 8bca526
Show file tree
Hide file tree
Showing 3 changed files with 189 additions and 47 deletions.
140 changes: 93 additions & 47 deletions src/utils.js
Expand Up @@ -347,6 +347,7 @@ async function createEvaluator(loaderContext, code, options) {
resolveToContext: true,
});

const resolvedImportDependencies = new Map();
const resolvedDependencies = new Map();
const seen = new Set();

Expand All @@ -361,6 +362,47 @@ async function createEvaluator(loaderContext, code, options) {
options
);

const optionsImports = [];

for (const importPath of options.imports) {
const isGlob = fastGlob.isDynamicPattern(importPath);

optionsImports.push({
importPath,
resolved: resolveFilename(
loaderContext,
fileResolve,
globResolve,
isGlob,
path.dirname(loaderContext.resourcePath),
importPath
),
});
}

await Promise.all(
optionsImports.map(async (result) => {
const { importPath } = result;
let { resolved } = result;

try {
resolved = await resolved;
} catch (ignoreError) {
return;
}

const isArray = Array.isArray(resolved);

// `stylus` returns forward slashes on windows
// eslint-disable-next-line no-param-reassign
result.resolved = isArray
? resolved.map((item) => path.normalize(item))
: path.normalize(resolved);

resolvedImportDependencies.set(importPath, result);
})
);

return class CustomEvaluator extends Evaluator {
visitImport(imported) {
this.return += 1;
Expand All @@ -372,64 +414,68 @@ async function createEvaluator(loaderContext, code, options) {

let webpackResolveError;

if (
node.name !== 'url' &&
nodePath &&
!URL_RE.test(nodePath) &&
// `imports` is not resolved, let's avoid extra actions
!this.options.imports.includes(nodePath)
) {
const dependencies = resolvedDependencies.get(
path.normalize(node.filename)
);
if (node.name !== 'url' && nodePath && !URL_RE.test(nodePath)) {
let dependency;

if (dependencies) {
const dependency = dependencies.find((item) => {
if (
item.originalLineno === node.lineno &&
item.originalColumn === node.column &&
item.originalNodePath === nodePath
) {
if (item.error) {
webpackResolveError = item.error;
} else {
return item.resolved;
}
}
const isEntrypoint = loaderContext.resourcePath === node.filename;

return false;
});
if (isEntrypoint) {
dependency = resolvedImportDependencies.get(nodePath);
}

if (dependency) {
const { resolved } = dependency;
if (!dependency) {
const dependencies = resolvedDependencies.get(
path.normalize(node.filename)
);

if (dependencies) {
dependency = dependencies.find((item) => {
if (
item.originalLineno === node.lineno &&
item.originalColumn === node.column &&
item.originalNodePath === nodePath
) {
if (item.error) {
webpackResolveError = item.error;
} else {
return item.resolved;
}
}

if (!Array.isArray(resolved)) {
// Avoid re globbing when resolved import contains glob characters
node.string = fastGlob.escapePath(resolved);
} else if (resolved.length > 0) {
let hasError = false;
return false;
});
}
}

const blocks = resolved.map((item) => {
const clonedImported = imported.clone();
const clonedNode = this.visit(clonedImported.path).first;
if (dependency) {
const { resolved } = dependency;

// Avoid re globbing when resolved import contains glob characters
clonedNode.string = fastGlob.escapePath(item);
if (!Array.isArray(resolved)) {
// Avoid re globbing when resolved import contains glob characters
node.string = fastGlob.escapePath(resolved);
} else if (resolved.length > 0) {
let hasError = false;

let result;
const blocks = resolved.map((item) => {
const clonedImported = imported.clone();
const clonedNode = this.visit(clonedImported.path).first;

try {
result = super.visitImport(clonedImported);
} catch (error) {
hasError = true;
}
// Avoid re globbing when resolved import contains glob characters
clonedNode.string = fastGlob.escapePath(item);

return result;
});
let result;

if (!hasError) {
return mergeBlocks(blocks);
try {
result = super.visitImport(clonedImported);
} catch (error) {
hasError = true;
}

return result;
});

if (!hasError) {
return mergeBlocks(blocks);
}
}
}
Expand Down
36 changes: 36 additions & 0 deletions test/__snapshots__/loader.test.js.snap
Expand Up @@ -338,6 +338,20 @@ exports[`loader imports files listed in option argument webpack style: errors 1`

exports[`loader imports files listed in option argument webpack style: warnings 1`] = `Array []`;

exports[`loader imports files listed in option argument with tilde: css 1`] = `
".not-real-nib {
color: #000;
}
.imported-stylus {
border: 5px;
}
"
`;

exports[`loader imports files listed in option argument with tilde: errors 1`] = `Array []`;

exports[`loader imports files listed in option argument with tilde: warnings 1`] = `Array []`;

exports[`loader imports files listed in option argument: css 1`] = `
"body {
background: url(\\"img.png\\");
Expand Down Expand Up @@ -402,6 +416,28 @@ exports[`loader imports files with special characters listed in glob: errors 1`]

exports[`loader imports files with special characters listed in glob: warnings 1`] = `Array []`;

exports[`loader imports in option "import" and webpack alias: css 1`] = `
".webpack-alias-1 {
color: #f00;
}
.webpack-alias-2 {
color: #ff7f50;
}
body {
font: 12px Helvetica, Arial, sans-serif;
}
a.button {
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
border-radius: 5px;
}
"
`;

exports[`loader imports in option "import" and webpack alias: errors 1`] = `Array []`;

exports[`loader imports in option "import" and webpack alias: warnings 1`] = `Array []`;

exports[`loader imports the right file based on context: css 1`] = `
".a-color {
color: #aaa;
Expand Down
60 changes: 60 additions & 0 deletions test/loader.test.js
Expand Up @@ -708,6 +708,35 @@ describe('loader', () => {
expect(getErrors(stats)).toMatchSnapshot('errors');
});

it('imports files listed in option argument with tilde', async () => {
const testId = './stylus.styl';
const compiler = getCompiler(
testId,
{
stylusOptions: {
import: ['~fakenib'],
},
},
{
resolve: {
modules: ['node_modules'],
},
}
);
const stats = await compile(compiler);
const codeFromBundle = getCodeFromBundle(stats, compiler);
const codeFromStylus = await getCodeFromStylus(testId, {
stylusOptions: {
import: ['~fakenib'],
},
});

expect(codeFromBundle.css).toBe(codeFromStylus.css);
expect(codeFromBundle.css).toMatchSnapshot('css');
expect(getWarnings(stats)).toMatchSnapshot('warnings');
expect(getErrors(stats)).toMatchSnapshot('errors');
});

it('imports files listed in option "style" package.json', async () => {
const testId = './import-fakestylus.styl';
const compiler = getCompiler(testId);
Expand Down Expand Up @@ -1246,6 +1275,37 @@ describe('loader', () => {
expect(getErrors(stats)).toMatchSnapshot('errors');
});

it('imports in option "import" and webpack alias', async () => {
const testId = './basic.styl';
const compiler = getCompiler(
testId,
{
stylusOptions: {
import: ['alias/1', '~alias/2'],
},
},
{
resolve: {
alias: {
alias: path.resolve(__dirname, 'fixtures', 'alias'),
},
},
}
);
const stats = await compile(compiler);
const codeFromBundle = getCodeFromBundle(stats, compiler);
const codeFromStylus = await getCodeFromStylus(testId, {
stylusOptions: {
import: ['alias/1', '~alias/2'],
},
});

expect(codeFromBundle.css).toBe(codeFromStylus.css);
expect(codeFromBundle.css).toMatchSnapshot('css');
expect(getWarnings(stats)).toMatchSnapshot('warnings');
expect(getErrors(stats)).toMatchSnapshot('errors');
});

it('imports the right file based on context', async () => {
const testId = './context';
const compiler = getCompiler(testId);
Expand Down

0 comments on commit 8bca526

Please sign in to comment.