Skip to content

Commit 9e72e67

Browse files
authored
Support multiple dist configurations and ESM (#13)
1 parent 00537fe commit 9e72e67

File tree

7 files changed

+138
-78
lines changed

7 files changed

+138
-78
lines changed

action/commands/build.d.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
export declare const distDir = "dist";
1+
export declare const DIST_DIR = "dist";
22
export declare const buildCommand: import("../command").CommandFactory<{}, {
33
single?: boolean | undefined;
44
}>;
5-
export declare function readPackageJson(baseDir: string): Promise<any>;
65
export declare function validatePackageJson(pkg: any): void;
76
//# sourceMappingURL=build.d.ts.map

action/config.d.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ export interface BobConfig {
1414
commands?: {
1515
[cmdName: string]: Command;
1616
};
17+
dists?: {
18+
distDir: string;
19+
distPath?: string;
20+
}[];
1721
}
1822
interface UseConfigOptions {
1923
config?: string;

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
"yargs": "15.3.1"
4242
},
4343
"scripts": {
44+
"prepublish": "yarn build",
4445
"build": "tsc && ncc build src/action.ts -o action"
4546
},
4647
"devDependencies": {

src/commands/build.ts

Lines changed: 91 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,14 @@ interface BuildOptions {
2020
bin?: Record<string, { input: string; sourcemap?: boolean }>;
2121
}
2222

23-
export const distDir = "dist";
23+
export const DIST_DIR = "dist";
24+
25+
interface PackageInfo {
26+
packagePath: string;
27+
cwd: string;
28+
pkg: any;
29+
fullName: string;
30+
}
2431

2532
export const buildCommand = createCommand<
2633
{},
@@ -42,31 +49,48 @@ export const buildCommand = createCommand<
4249
});
4350
},
4451
async handler(args) {
52+
config.dists = config.dists || [
53+
{
54+
distDir: DIST_DIR,
55+
distPath: ''
56+
}
57+
];
4558
if (args.single) {
46-
await buildSingle();
59+
await buildSingle({ distDir: DIST_DIR });
4760
return;
4861
}
4962

5063
const limit = pLimit(4);
5164
const packages = await globby("packages/**/package.json", {
5265
cwd: process.cwd(),
5366
absolute: true,
54-
ignore: ["**/node_modules/**", `**/${distDir}/**`],
67+
ignore: ["**/node_modules/**", ...config.dists.map(({ distDir }) => `**/${distDir}/**`)],
5568
});
5669

57-
await Promise.all(
58-
packages.map((packagePath) =>
59-
limit(() => build(packagePath, config, reporter))
60-
)
70+
const packageInfoList: PackageInfo[] = await Promise.all(
71+
packages.map(packagePath => limit(async () => {
72+
const cwd = packagePath.replace("/package.json", "");
73+
const pkg = await fs.readJSON(resolve(cwd, 'package.json'));
74+
const fullName: string = pkg.name;
75+
return { packagePath, cwd, pkg, fullName };
76+
}))
6177
);
78+
79+
for (const { distDir, distPath } of config.dists) {
80+
await Promise.all(
81+
packageInfoList.map(({ packagePath, cwd, pkg, fullName }) =>
82+
limit(() => build({ packagePath, cwd, pkg, fullName, config, reporter, distDir, distPath, packageInfoList }))
83+
)
84+
);
85+
}
6286
},
6387
};
6488
});
6589

66-
async function buildSingle() {
90+
async function buildSingle({ distDir, distPath = '' }: { distDir: string; distPath?: string; }) {
6791
const cwd = process.cwd();
6892
const packagePath = join(process.cwd(), "package.json");
69-
const pkg = await readPackageJson(cwd);
93+
const pkg = await fs.readJSON(packagePath);
7094

7195
validatePackageJson(pkg);
7296

@@ -87,7 +111,7 @@ async function buildSingle() {
87111
}),
88112
typescript(),
89113
generatePackageJson({
90-
baseContents: rewritePackageJson(pkg),
114+
baseContents: rewritePackageJson(pkg, distPath),
91115
additionalDependencies: Object.keys(pkg.dependencies || {}),
92116
}),
93117
],
@@ -108,24 +132,16 @@ async function buildSingle() {
108132
const generates = [
109133
{
110134
...commonOutputOptions,
111-
file: join(distDir, "index.cjs.js"),
135+
file: join(distDir, "index.js"),
112136
format: "cjs" as const,
113137
},
114138
{
115139
...commonOutputOptions,
116-
file: join(distDir, "index.esm.js"),
140+
file: join(distDir, "index.mjs"),
117141
format: "esm" as const,
118142
},
119143
];
120144

121-
if (pkg.exports) {
122-
generates.push({
123-
...commonOutputOptions,
124-
file: join(distDir, "index.mjs"),
125-
format: "esm" as const,
126-
});
127-
}
128-
129145
await Promise.all(
130146
generates.map(async (outputOptions) => {
131147
await bundle.write(outputOptions);
@@ -135,26 +151,22 @@ async function buildSingle() {
135151
// move README.md and LICENSE
136152
await copyToDist(
137153
cwd,
138-
["README.md", "LICENSE"].concat(buildOptions?.copy || [])
154+
["README.md", "LICENSE"].concat(buildOptions?.copy || []),
155+
DIST_DIR + distPath
139156
);
140157
}
141158

142159
async function build(
143-
packagePath: string,
144-
config: BobConfig,
145-
reporter: Consola
160+
{ packagePath, cwd, pkg, fullName, config, reporter, distDir, distPath = '', packageInfoList }: { packagePath: string; cwd: string; pkg: any; fullName: string; config: BobConfig; reporter: Consola; distDir: string; distPath?: string; packageInfoList: PackageInfo[] },
146161
) {
147162
const scope = config.scope;
148-
const cwd = packagePath.replace("/package.json", "");
149-
const pkg = await readPackageJson(cwd);
150-
const fullName: string = pkg.name;
151163

152164
if ((config.ignore || []).includes(fullName)) {
153165
reporter.warn(`Ignored ${fullName}`);
154166
return;
155167
}
156168

157-
const name = fullName.replace(`${scope}/`, "");
169+
const name = fullName.replace(`${scope}/`, distPath);
158170

159171
validatePackageJson(pkg);
160172

@@ -184,7 +196,7 @@ async function build(
184196
packageJSONPath: packagePath,
185197
}),
186198
generatePackageJson({
187-
baseContents: rewritePackageJson(pkg),
199+
baseContents: rewritePackageJson(pkg, distPath),
188200
additionalDependencies: Object.keys(pkg.dependencies || {}),
189201
}),
190202
],
@@ -205,24 +217,16 @@ async function build(
205217
const generates = [
206218
{
207219
...commonOutputOptions,
208-
file: join(bobProjectDir, "index.cjs.js"),
220+
file: join(bobProjectDir, "index.js"),
209221
format: "cjs" as const,
210222
},
211223
{
212224
...commonOutputOptions,
213-
file: join(bobProjectDir, "index.esm.js"),
225+
file: join(bobProjectDir, "index.mjs"),
214226
format: "esm" as const,
215227
},
216228
];
217229

218-
if (pkg.exports) {
219-
generates.push({
220-
...commonOutputOptions,
221-
file: join(distDir, "index.mjs"),
222-
format: "esm" as const,
223-
});
224-
}
225-
226230
const declarations = await globby("**/*.d.ts", {
227231
cwd: distProjectSrcDir,
228232
ignore: ["**/node_modules/**"],
@@ -269,37 +273,45 @@ async function build(
269273
banner: `#!/usr/bin/env node`,
270274
preferConst: true,
271275
sourcemap: options.sourcemap,
272-
file: join(bobProjectDir, pkg.bin[alias].replace(`${distDir}/`, "")),
276+
file: join(bobProjectDir, pkg.bin[alias].replace(`${DIST_DIR}/`, "")),
273277
format: "cjs",
274278
});
275279
})
276280
);
277281
}
278282

279283
// remove <project>/dist
280-
await fs.remove(join(cwd, distDir));
284+
await fs.remove(join(cwd, DIST_DIR + distPath));
285+
286+
// fix distPath import in extra dists
287+
function replaceAll(str: string, from: string, to: string) {
288+
return str.split(from).join(to);
289+
}
290+
if (distPath) {
291+
await Promise.all(
292+
generates.map(({ file }) => limit(async () => {
293+
let content = await fs.readFile(file, 'utf8');
294+
for (const { fullName } of packageInfoList) {
295+
content = replaceAll(content, `'${fullName}'`, `'${fullName}${distPath}'`);
296+
}
297+
await fs.writeFile(file, content, { encoding: 'utf8', flag: 'w' });
298+
}))
299+
)
300+
}
301+
281302
// move bob/<project-name> to <project>/dist
282-
await fs.move(bobProjectDir, join(cwd, distDir));
303+
await fs.move(bobProjectDir, join(cwd, DIST_DIR + distPath));
283304
// move README.md and LICENSE
284305
await copyToDist(
285306
cwd,
286-
["README.md", "LICENSE"].concat(pkg.buildOptions?.copy || [])
307+
["README.md", "LICENSE"].concat(pkg.buildOptions?.copy || []),
308+
DIST_DIR + distPath
287309
);
288310

289311
reporter.success(`Built ${pkg.name}`);
290312
}
291313

292-
//
293-
294-
export async function readPackageJson(baseDir: string) {
295-
return JSON.parse(
296-
await fs.readFile(resolve(baseDir, "package.json"), {
297-
encoding: "utf-8",
298-
})
299-
);
300-
}
301-
302-
function rewritePackageJson(pkg: Record<string, any>) {
314+
function rewritePackageJson(pkg: Record<string, any>, distPath: string) {
303315
const newPkg: Record<string, any> = {};
304316
const fields = [
305317
"name",
@@ -323,18 +335,30 @@ function rewritePackageJson(pkg: Record<string, any>) {
323335
}
324336
});
325337

326-
newPkg.main = "index.cjs.js";
327-
newPkg.module = "index.esm.js";
338+
newPkg.name += distPath;
339+
newPkg.main = "index.js";
340+
newPkg.module = "index.mjs";
328341
newPkg.typings = "index.d.ts";
329342
newPkg.typescript = {
330343
definition: newPkg.typings,
331344
};
332345

346+
newPkg.exports = {
347+
".": {
348+
require: "./index.js",
349+
import: "./index.mjs",
350+
},
351+
"./*": {
352+
require: "./*.js",
353+
import: "./*.mjs",
354+
},
355+
};
356+
333357
if (pkg.bin) {
334358
newPkg.bin = {};
335359

336360
for (const alias in pkg.bin) {
337-
newPkg.bin[alias] = pkg.bin[alias].replace(`${distDir}/`, "");
361+
newPkg.bin[alias] = pkg.bin[alias].replace(`${DIST_DIR}/`, "");
338362
}
339363
}
340364

@@ -352,18 +376,19 @@ export function validatePackageJson(pkg: any) {
352376
}
353377
}
354378

355-
expect("main", `${distDir}/index.cjs.js`);
356-
expect("module", `${distDir}/index.esm.js`);
357-
expect("typings", `${distDir}/index.d.ts`);
358-
expect("typescript.definition", `${distDir}/index.d.ts`);
379+
expect("main", `${DIST_DIR}/index.js`);
380+
expect("module", `${DIST_DIR}/index.mjs`);
381+
expect("typings", `${DIST_DIR}/index.d.ts`);
382+
expect("typescript.definition", `${DIST_DIR}/index.d.ts`);
359383

360-
if (pkg.exports) {
361-
expect("exports.require", `./${pkg.main}`);
362-
expect("exports.default", `./${distDir}/index.mjs`);
363-
}
384+
expect("exports['.'].require", `./${DIST_DIR}/index.js`);
385+
expect("exports['.'].import", `./${DIST_DIR}/index.mjs`);
386+
387+
expect("exports['./*'].require", `./${DIST_DIR}/*.js`);
388+
expect("exports['./*'].import", `./${DIST_DIR}/*.mjs`);
364389
}
365390

366-
async function copyToDist(cwd: string, files: string[]) {
391+
async function copyToDist(cwd: string, files: string[], distDir: string) {
367392
const allFiles = await globby(files, { cwd });
368393

369394
return Promise.all(

src/commands/pack-flat.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
import globby from "globby";
22
import pLimit from "p-limit";
3-
import fs from "fs-extra";
3+
import fs, { readJSON } from "fs-extra";
44
import { resolve, join } from "path";
55
import { execSync } from "child_process";
66
import { Consola } from "consola";
77
import { paramCase } from "param-case";
88

99
import { createCommand } from "../command";
1010
import { BobConfig } from "../config";
11-
import { readPackageJson, distDir } from "./build";
11+
import { DIST_DIR } from "./build";
1212

1313
export const packFlatCommand = createCommand<
1414
{},
@@ -36,7 +36,7 @@ export const packFlatCommand = createCommand<
3636
const packages = await globby("packages/**/package.json", {
3737
cwd: process.cwd(),
3838
absolute: true,
39-
ignore: ["**/node_modules/**", `**/${distDir}/**`],
39+
ignore: ["**/node_modules/**", `**/${DIST_DIR}/**`],
4040
});
4141

4242
await Promise.all(
@@ -50,19 +50,19 @@ export const packFlatCommand = createCommand<
5050

5151
async function pack(packagePath: string, commit: string, config: BobConfig, reporter: Consola) {
5252
const cwd = packagePath.replace("/package.json", "");
53-
const pkg = await readPackageJson(cwd);
53+
const pkg = await readJSON(packagePath);
5454
const fullName: string = pkg.name;
5555

5656
if ((config.ignore || []).includes(fullName)) {
5757
reporter.warn(`Ignored ${fullName}`);
5858
return;
5959
}
6060

61-
const projectDistDir = join(cwd, distDir);
61+
const projectDistDir = join(cwd, DIST_DIR);
6262
const bobDir = resolve(process.cwd(), ".bob-packed");
6363

6464
// replace version to 0.0.0-canary-${commit}
65-
const distPkg = await readPackageJson(projectDistDir);
65+
const distPkg = await readJSON(join(projectDistDir, 'package.json'));
6666
const version = `0.0.0-canary-${commit}`;
6767
distPkg.version = version;
6868
await fs.writeFile(join(projectDistDir, 'package.json'), JSON.stringify(distPkg, null, 2), {

0 commit comments

Comments
 (0)