-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Expand file tree
/
Copy pathplugin.ts
More file actions
108 lines (97 loc) Β· 3.83 KB
/
Copy pathplugin.ts
File metadata and controls
108 lines (97 loc) Β· 3.83 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
import chalk from 'chalk';
import {Command, UsageError} from 'clipanion';
import filesize from 'filesize';
import fs from 'fs';
import path from 'path';
import {RawSource} from 'webpack-sources';
import webpack from 'webpack';
import {isDynamicLib} from '../../data/dynamicLibs';
import {makeConfig} from '../../tools/makeConfig';
import {reindent} from '../../tools/reindent';
// The name gets normalized so that everyone can override some plugins by
// their own (@arcanis/yarn-plugin-foo would override @yarnpkg/plugin-foo
// as well as @mael/yarn-plugin-foo)
const getNormalizedName = (name: string) => {
const parsing = name.match(/^(?:@yarnpkg\/|(?:@[^\/]+\/)?yarn-)(plugin-[^\/]+)/);
if (parsing === null)
throw new UsageError(`Invalid plugin name "${name}" - it should be "yarn-plugin-<something>"`);
return `@yarnpkg/${parsing[1]}`;
};
// eslint-disable-next-line arca/no-default-export
export default class BuildPluginCommand extends Command {
static usage = Command.Usage({
description: `build the local plugin`,
});
@Command.Path(`build`, `plugin`)
async execute() {
const basedir = process.cwd();
const {name: rawName} = require(`${basedir}/package.json`);
const name = getNormalizedName(rawName);
const output = `${basedir}/bundles/${name}.js`;
const compiler = webpack(makeConfig({
context: basedir,
entry: `.`,
output: {
filename: path.basename(output),
path: path.dirname(output),
libraryTarget: `var`,
library: `plugin`,
},
externals: [
(context: any, request: string, callback: any) => {
if (request !== name && isDynamicLib(request)) {
callback(null, `commonjs ${request}`);
} else {
callback();
}
},
],
plugins: [
// This plugin wraps the generated bundle so that it doesn't actually
// get evaluated right now - until after we give it a custom require
// function that will be able to fetch the dynamic modules.
{apply: (compiler: webpack.Compiler) => {
compiler.hooks.compilation.tap(`MyPlugin`, (compilation: webpack.compilation.Compilation) => {
compilation.hooks.optimizeChunkAssets.tap(`MyPlugin`, (chunks: Array<webpack.compilation.Chunk>) => {
for (const chunk of chunks) {
for (const file of chunk.files) {
compilation.assets[file] = new RawSource(reindent(`
/* eslint-disable*/
module.exports = {
name: ${JSON.stringify(name)},
factory: function (require) {
${reindent(compilation.assets[file].source().replace(/^ +/, ``), 11)}
return plugin;
},
};
`));
}
}
});
});
}},
],
}));
const buildErrors = await new Promise<string | null>((resolve, reject) => {
compiler.run((err, stats) => {
if (err) {
reject(err);
} else if (stats.compilation.errors.length > 0) {
resolve(stats.toString(`errors-only`));
} else {
resolve(null);
}
});
});
if (buildErrors !== null) {
this.context.stdout.write(`${chalk.red(`β`)} Failed to build ${name}:\n`);
this.context.stdout.write(`${buildErrors}\n`);
return 1;
} else {
this.context.stdout.write(`${chalk.green(`β`)} Done building ${name}!\n`);
this.context.stdout.write(`${chalk.cyan(`?`)} Bundle path: ${output}\n`);
this.context.stdout.write(`${chalk.cyan(`?`)} Bundle size: ${filesize(fs.statSync(output).size)}\n`);
return 0;
}
}
}