Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(plugin): support for esm loader which its ext is .js in rspack #393

Merged
merged 1 commit into from
Jun 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading