Skip to content

Commit

Permalink
feat(plugin): support for esm loader which its ext is .js
Browse files Browse the repository at this point in the history
  • Loading branch information
easy1090 committed Jun 27, 2024
1 parent cc65ee4 commit 1757369
Show file tree
Hide file tree
Showing 19 changed files with 233 additions and 709 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { parseQuery } from 'loader-utils';

/**
* @type {import("webpack").LoaderDefinitionFunction<{}, {}>}
*/
const loader = (input) => {
const res = [input, `// ${JSON.stringify(this?.query || '')}`];

// Based on https://github.com/windicss/windicss-webpack-plugin/blob/main/src/loaders/windicss-template.ts#L42
// test the loader query
if (this?.query && this?.query !== '') {
res.push(`// ${JSON.stringify(parseQuery(this?.query))}`);
}

return res.join('\n');
};

export default loader;
5 changes: 5 additions & 0 deletions e2e/cases/doctor-rspack/fixtures/loaders/esm/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "@test/rsdoctor-rspack",
"version": "0.1.0",
"type": "module"
}
6 changes: 5 additions & 1 deletion e2e/cases/doctor-rspack/plugin.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ async function rspackCompile(tapName: string, compile: typeof compileByRspack) {
__dirname,
'./fixtures/loaders/esm-serialize-query-to-comment.mjs',
);
const esmLoaderJs = path.resolve(
__dirname,
'./fixtures/loaders/esm/esm-serialize-query-to-comment.js',
);
const res = await compile(file, {
resolve: {
extensions: ['.ts', '.js'],
Expand All @@ -27,7 +31,7 @@ async function rspackCompile(tapName: string, compile: typeof compileByRspack) {
},
{
test: /\.js/,
use: esmLoader,
use: esmLoaderJs,
},
{
test: /\.[jt]s$/,
Expand Down
72 changes: 46 additions & 26 deletions packages/core/src/build-utils/build/utils/loader.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { getOptions } from 'loader-utils';
import path from 'path';
import fse from 'fs-extra';
import { omit, findIndex } from 'lodash';
import { Loader } from '@rsdoctor/utils/common';
import type { Common, Plugin } from '@rsdoctor/types';
import { Rule, SourceMapInput as WebpackSourceMapInput } from '../../../types';
import { readPackageJson } from '@rsdoctor/graph';

// webpack https://github.com/webpack/webpack/blob/2953d23a87d89b3bd07cf73336ee34731196c62e/lib/util/identifier.js#L311
// rspack https://github.com/web-infra-dev/rspack/blob/d22f049d4bce4f8ac20c1cbabeab3706eddaecc1/packages/rspack/src/loader-runner/index.ts#L47
Expand Down Expand Up @@ -167,39 +169,67 @@ export function mapEachRules<T extends Plugin.BuildRuleSetRule>(
});
}

function getLoaderNameMatch(
_r: Plugin.BuildRuleSetRule,
export function isESMLoader(r: Plugin.BuildRuleSetRule) {
let _loaderName =
typeof r === 'object' && typeof r?.loader === 'string'
? r.loader
: typeof r === 'string'
? r
: '';
if (!_loaderName) return false;
const isPath =
_loaderName.startsWith('/') ||
_loaderName.startsWith('./') ||
_loaderName.startsWith('../');
if (isPath) {
// Code to handle loaderName as a path address
const packageJsonData = readPackageJson(_loaderName, (file) => {
try {
return fse.readJsonSync(file, { encoding: 'utf8' });
} catch (e) {
// console.log(e)
}
});

if (packageJsonData?.type === 'module') return true;
}

// Code to handle loaderName as a loader name
return false;
}

export function getLoaderNameMatch(
r: Plugin.BuildRuleSetRule,
loaderName: string,
strict = true,
) {
if (!strict) {
return (
(typeof _r === 'object' &&
typeof _r?.loader === 'string' &&
_r.loader.includes(loaderName)) ||
(typeof _r === 'string' && (_r as string).includes(loaderName))
(typeof r === 'object' &&
typeof r?.loader === 'string' &&
r.loader.includes(loaderName)) ||
(typeof r === 'string' && (r as string).includes(loaderName))
);
}

return (
(typeof _r === 'object' &&
typeof _r?.loader === 'string' &&
_r.loader === loaderName) ||
(typeof _r === 'string' && _r === loaderName)
(typeof r === 'object' &&
typeof r?.loader === 'string' &&
r.loader === loaderName) ||
(typeof r === 'string' && r === loaderName)
);
}

// FIXME: Type BuildRuleSetRule maybe need optimize.
export function addProbeLoader2Rules<T extends Plugin.BuildRuleSetRule>(
rules: T[],
loaderName: string,
appendRules: (rule: T, index: number) => T,
strict?: boolean,
fn: (r: Plugin.BuildRuleSetRule) => boolean,
): T[] {
return rules.map((rule) => {
if (!rule || typeof rule === 'string') return rule;

if (getLoaderNameMatch(rule, loaderName, strict)) {
if (fn(rule)) {
const _rule = {
...rule,
use: [
Expand All @@ -216,9 +246,7 @@ export function addProbeLoader2Rules<T extends Plugin.BuildRuleSetRule>(

if (rule.use) {
if (Array.isArray(rule.use)) {
const _index = findIndex(rule.use, (_r) =>
getLoaderNameMatch(_r as T, loaderName, strict),
);
const _index = findIndex(rule.use, (_r) => fn(_r as T));
if (_index > -1) {
return appendRules(rule, _index);
}
Expand All @@ -239,22 +267,14 @@ export function addProbeLoader2Rules<T extends Plugin.BuildRuleSetRule>(
if ('oneOf' in rule && rule.oneOf) {
return {
...rule,
oneOf: addProbeLoader2Rules<T>(
rule.oneOf as T[],
loaderName,
appendRules,
),
oneOf: addProbeLoader2Rules<T>(rule.oneOf as T[], appendRules, fn),
};
}

if ('rules' in rule && rule.rules) {
return {
...rule,
rules: addProbeLoader2Rules<T>(
rule.rules as T[],
loaderName,
appendRules,
),
rules: addProbeLoader2Rules<T>(rule.rules as T[], appendRules, fn),
};
}
return rule;
Expand Down
11 changes: 8 additions & 3 deletions packages/core/src/inner-plugins/utils/loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { getSDK } from './sdk';
import { checkCirclePath } from './circleDetect';
import { ProxyLoaderInternalOptions, ProxyLoaderOptions } from '@/types';
import { Utils as BuildUtils, Types } from '@/build-utils/build';
import { isESMLoader } from '@/build-utils/build/utils';

export function getInternalLoaderOptions(
loaderContext: Plugin.LoaderContext<ProxyLoaderOptions>,
Expand Down Expand Up @@ -105,7 +106,11 @@ export function interceptLoader<T extends Plugin.BuildRuleSetRule>(
};

return BuildUtils.mapEachRules(rules, (rule) => {
if (rule.loader?.startsWith('builtin:') || rule.loader?.endsWith('.mjs')) {
if (
rule.loader?.startsWith('builtin:') ||
rule.loader?.endsWith('.mjs') ||
isESMLoader(rule)
) {
return rule;
}
const opts: ProxyLoaderOptions = {
Expand Down Expand Up @@ -183,8 +188,8 @@ export async function reportLoader(
const data: Types.SourceMap = !sourceMap
? {}
: isString(sourceMap)
? JSON.parse(sourceMap)
: sourceMap;
? JSON.parse(sourceMap)
: sourceMap;
const sourceMapData = {
version: data.version ?? -1,
sources: data.sources ?? [],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { addProbeLoader2Rules } from '@/build-utils/build/utils/loader';
import { describe, it, expect } from 'vitest';
import { Plugin } from '@rsdoctor/types';
import { Utils } from '@/build-utils/build';

const rules = [
{
Expand Down Expand Up @@ -95,7 +96,9 @@ const appendRule = (rule: Plugin.RuleSetRule, index: number) => {
describe('test src/build/utils/loader.ts addProbeLoader2Rules', () => {
it('addProbeLoader2Rules()', () => {
expect(
addProbeLoader2Rules(rules, 'builtin:swc-loader', appendRule),
addProbeLoader2Rules(rules, appendRule, (r: Plugin.BuildRuleSetRule) =>
Utils.getLoaderNameMatch(r, 'builtin:swc-loader', true),
),
).toMatchSnapshot();
});
});
1 change: 1 addition & 0 deletions packages/graph/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"@types/estree": "1.0.0",
"@types/lodash": "^4.17.0",
"@types/node": "^16",
"fs-extra": "^11.1.1",
"tslib": "2.4.1",
"typescript": "^5.2.2"
},
Expand Down
41 changes: 2 additions & 39 deletions packages/graph/src/graph/package-graph/graph.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { unionBy } from 'lodash';
import { dirname, join, resolve } from 'path';
import { SDK } from '@rsdoctor/types';
import { Package as PackageUtil } from '@rsdoctor/utils/common';
import type { ModuleGraph, Module } from '../module-graph';
import { Package } from './package';
import { PackageDependency } from './dependency';
import { readPackageJson } from './utils';
export { readPackageJson } from './utils';

export class PackageGraph implements SDK.PackageGraphInstance {
static fromModuleGraph(
Expand Down Expand Up @@ -84,44 +85,6 @@ export class PackageGraph implements SDK.PackageGraphInstance {
return pkg;
}

// TODO: add test for this function.
const readPackageJson = (
file: string,
readFile?: SDK.GetPackageFile,
): SDK.PackageBasicData | undefined => {
let result: SDK.PackageJSONData | undefined;
let current = file;

while (current !== '/' && !result) {
if (dirname(current) === current) {
break;
}
current = dirname(current);
if (readFile) {
result = readFile(join(current, 'package.json'));
}
if (!readFile) {
result = PackageUtil.getPackageMetaFromModulePath(file);
} else if (!result?.name) {
result = undefined;
}
}

if (!result) {
return;
}

// Some packages will put an empty package.json in the source folder.
if (readFile && (!result.name || !result.version)) {
return readPackageJson(dirname(current), readFile);
}

return {
...result,
root: current,
};
};

const cache = this.getPackageContainFile(file);

if (cache) {
Expand Down
42 changes: 42 additions & 0 deletions packages/graph/src/graph/package-graph/utils.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,45 @@
import { dirname, join } from 'path';
import { SDK } from '@rsdoctor/types';
import { Package as PackageUtil } from '@rsdoctor/utils/common';

export function isPackagePath(path: string) {
return /(^|[/\\])node_modules[/\\]/.test(path);
}

// TODO: add test for this function.
export const readPackageJson = (
file: string,
readFile?: SDK.GetPackageFile,
): SDK.PackageBasicData | undefined => {
let result: SDK.PackageJSONData | undefined;
let current = file;

while (current !== '/' && !result) {
if (dirname(current) === current) {
break;
}
current = dirname(current);
if (readFile) {
result = readFile(join(current, 'package.json'));
}
if (!readFile) {
result = PackageUtil.getPackageMetaFromModulePath(file);
} else if (!result?.name) {
result = undefined;
}
}

if (!result) {
return;
}

// Some packages will put an empty package.json in the source folder.
if (readFile && (!result.name || !result.version)) {
return readPackageJson(dirname(current), readFile);
}

return {
...result,
root: current,
};
};
Loading

0 comments on commit 1757369

Please sign in to comment.