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(vue): support for the loader analysis with vue-loader #467

Merged
merged 2 commits into from
Sep 23, 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
5 changes: 5 additions & 0 deletions .changeset/sharp-elephants-build.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@rsdoctor/rspack-plugin': patch
---

feat(loader): support for the loaders analysis with vue-loader
1 change: 1 addition & 0 deletions packages/core/src/build-utils/build/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * as Chunks from './chunks';
export * as Utils from './utils';
export * as Loader from './loader';
export * as Types from '../../types';
export * as ModuleGraph from './module-graph';
1 change: 1 addition & 0 deletions packages/core/src/build-utils/build/loader/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './probeLoaderPlugin';
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { getSDK } from '@rsdoctor/core/plugins';
import { Build } from '@/build-utils';
import { getSDK } from '@/inner-plugins';
import { Plugin, SDK } from '@rsdoctor/types';
import { Build } from '@rsdoctor/core';
import type { LoaderDefinitionFunction } from '@rspack/core';
import { omit } from 'lodash';
import path from 'path';

const loaderModule: Plugin.LoaderDefinition<
export const loaderModule: Plugin.LoaderDefinition<
Parameters<LoaderDefinitionFunction>,
{}
> = function (...args) {
Expand Down
61 changes: 61 additions & 0 deletions packages/core/src/build-utils/build/loader/probeLoaderPlugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { Plugin } from '@rsdoctor/types';
import type { RuleSetRules } from '@rspack/core';
import { Loader } from '@rsdoctor/utils/common';
import { Build } from '@/build-utils';
import { Utils } from '..';

const BuiltinLoaderName = 'builtin:swc-loader';
SoonIter marked this conversation as resolved.
Show resolved Hide resolved
const ESMLoaderFile = '.mjs';

export class ProbeLoaderPlugin {
apply(compiler: Plugin.BaseCompiler) {
compiler.hooks.beforeRun.tap(
{
name: 'ProbeLoaderPlugin',
},
() => {
this.addProbeLoader(compiler);
},
);

compiler.hooks.watchRun.tap(
{
name: 'ProbeLoaderPlugin',
},
() => {
this.addProbeLoader(compiler);
},
);
}

private addProbeLoader(compiler: Plugin.BaseCompiler) {
let rules = compiler.options.module.rules as Plugin.RuleSetRule[];

if (Loader.isVue(compiler)) {
compiler.options.module.rules = Utils.addProbeLoader2Rules(
rules,
compiler,
(r: Plugin.BuildRuleSetRule) => !!r.loader || typeof r === 'string',
) as RuleSetRules;
return;
}

rules = Utils.addProbeLoader2Rules(
rules,
compiler,
(r: Plugin.BuildRuleSetRule) =>
Build.Utils.getLoaderNameMatch(r, BuiltinLoaderName, true),
) as Plugin.RuleSetRule[];

compiler.options.module.rules = Utils.addProbeLoader2Rules(
rules,
compiler,
(r: Plugin.BuildRuleSetRule) => {
return (
Build.Utils.getLoaderNameMatch(r, ESMLoaderFile, false) ||
Build.Utils.isESMLoader(r)
);
},
) as RuleSetRules;
}
}
1 change: 1 addition & 0 deletions packages/core/src/build-utils/build/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './loader';
export * from './plugin';
export * from './parseBundle';
export * from '../loader/probeLoader';
116 changes: 86 additions & 30 deletions packages/core/src/build-utils/build/utils/loader.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import path from 'path';
import fse from 'fs-extra';
import { omit, findIndex } from 'lodash';
import { omit } 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';
import { RuleSetUseItem } from '@rspack/core';
import { RuleSetUseItem as WebpackRuleSetUseItem } from 'webpack';
import { debug } from '@rsdoctor/utils/logger';

// 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 @@ -90,8 +93,8 @@ export function mapEachRules<T extends Plugin.BuildRuleSetRule>(
}

// https://webpack.js.org/configuration/module/#ruleloaders
if (Array.isArray((rule as unknown as Rule).loaders)) {
const { loaders, ...rest } = rule as unknown as Rule;
if (Array.isArray((rule as Rule).loaders)) {
const { loaders, ...rest } = rule as Rule;
return {
...(rest as Plugin.RuleSetRule),
use: mapEachRules(loaders as T[], callback),
Expand Down Expand Up @@ -119,7 +122,7 @@ export function mapEachRules<T extends Plugin.BuildRuleSetRule>(
const newRule = {
...rule,
use: (...args: any) => {
const rules = funcUse.apply(null, args) as any;
const rules = funcUse.apply(null, args) as T[];
return mapEachRules(rules, callback);
},
};
Expand Down Expand Up @@ -176,7 +179,7 @@ export function isESMLoader(r: Plugin.BuildRuleSetRule) {
try {
return fse.readJsonSync(file, { encoding: 'utf8' });
} catch (e) {
// console.log(e)
debug(() => `isESMLoader function error:${e}`);
}
});

Expand All @@ -187,6 +190,40 @@ export function isESMLoader(r: Plugin.BuildRuleSetRule) {
return false;
}

function appendProbeLoaders(
compiler: Plugin.BaseCompiler,
loaderConfig: RuleSetUseItem | WebpackRuleSetUseItem,
): RuleSetUseItem[] {
const _options =
typeof loaderConfig === 'object'
? typeof loaderConfig.options === 'string'
? { options: loaderConfig.options }
: loaderConfig.options
: {};
const loaderPath = path.join(__dirname, '../loader/probeLoader.js');
const loader =
typeof loaderConfig === 'string'
? loaderConfig
: typeof loaderConfig === 'object' && loaderConfig.loader;

const createProbeLoader = (type: 'start' | 'end') => ({
loader: loaderPath,
options: {
..._options,
loader,
ident: undefined,
type,
builderName: compiler.options.name,
},
});

return [
createProbeLoader('end'),
loaderConfig as RuleSetUseItem,
createProbeLoader('start'),
];
}

export function getLoaderNameMatch(
r: Plugin.BuildRuleSetRule,
loaderName: string,
Expand All @@ -200,72 +237,91 @@ export function getLoaderNameMatch(
(typeof r === 'string' && (r as string).includes(loaderName))
);
}

return (
(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[],
appendRules: (rule: T, index: number) => T,
compiler: Plugin.BaseCompiler,
fn: (r: Plugin.BuildRuleSetRule) => boolean,
): T[] {
return rules.map((rule) => {
if (!rule || typeof rule === 'string') return rule;

if (fn(rule)) {
const _rule = {
// Handle single loader case
if (fn(rule) && !rule.use) {
const loaderConfig: RuleSetUseItem = {
loader: rule.loader ?? '',
options: rule.options,
ident:
'ident' in rule && typeof rule.ident === 'string'
? rule.ident
: undefined,
};
return {
...rule,
use: [
{
loader: rule.loader,
options: rule.options,
},
],
use: appendProbeLoaders(compiler, loaderConfig),
loader: undefined,
options: undefined,
};
return appendRules(_rule, 0);
}

// Handle 'use' property
if (rule.use) {
if (Array.isArray(rule.use)) {
const _index = findIndex(rule.use, (_r) => fn(_r as T));
if (_index > -1) {
return appendRules(rule, _index);
}
rule.use = rule.use.flatMap((loaderConfig) => {
if (
typeof loaderConfig === 'string' ||
(typeof loaderConfig === 'object' &&
loaderConfig &&
'loader' in loaderConfig)
) {
return fn(loaderConfig as T)
? appendProbeLoaders(compiler, loaderConfig)
: [loaderConfig];
}
return [loaderConfig];
});
} else if (
typeof rule.use === 'object' &&
!Array.isArray(rule.use) &&
typeof rule.use !== 'function'
) {
rule.use = [
{
...rule.use,
},
];
return appendRules(rule, 0);
if ('loader' in rule.use) {
rule.use = fn(rule.use as T)
? appendProbeLoaders(compiler, rule.use)
: [rule.use];
}
} else if (typeof rule.use === 'string') {
rule.use = fn(rule.use as unknown as T)
? appendProbeLoaders(compiler, { loader: rule.use })
: [
{
loader: rule.use,
},
];
}
}

// Handle nested rules
if ('oneOf' in rule && rule.oneOf) {
return {
...rule,
oneOf: addProbeLoader2Rules<T>(rule.oneOf as T[], appendRules, fn),
oneOf: addProbeLoader2Rules<T>(rule.oneOf as T[], compiler, fn),
};
}

if ('rules' in rule && rule.rules) {
return {
...rule,
rules: addProbeLoader2Rules<T>(rule.rules as T[], appendRules, fn),
rules: addProbeLoader2Rules<T>(rule.rules as T[], compiler, fn),
};
}

return rule;
});
}
Expand Down
Loading
Loading