Skip to content

Commit

Permalink
feat(vue): support for the loader analysis with vue-loader (#467)
Browse files Browse the repository at this point in the history
  • Loading branch information
easy1090 authored Sep 23, 2024
1 parent 3e602a5 commit 76dc27c
Show file tree
Hide file tree
Showing 14 changed files with 650 additions and 197 deletions.
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';
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

0 comments on commit 76dc27c

Please sign in to comment.