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

Support multiple dist configurations and ESM #13

Merged
merged 9 commits into from
Jun 9, 2021
Merged
Show file tree
Hide file tree
Changes from 8 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
3 changes: 1 addition & 2 deletions action/commands/build.d.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
export declare const distDir = "dist";
export declare const DIST_DIR = "dist";
export declare const buildCommand: import("../command").CommandFactory<{}, {
single?: boolean | undefined;
}>;
export declare function readPackageJson(baseDir: string): Promise<any>;
export declare function validatePackageJson(pkg: any): void;
//# sourceMappingURL=build.d.ts.map
4 changes: 4 additions & 0 deletions action/config.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ export interface BobConfig {
commands?: {
[cmdName: string]: Command;
};
dists?: {
distDir: string;
distPath?: string;
}[];
}
interface UseConfigOptions {
config?: string;
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"yargs": "15.3.1"
},
"scripts": {
"prepublish": "yarn build",
"build": "tsc && ncc build src/action.ts -o action"
},
"devDependencies": {
Expand Down
157 changes: 91 additions & 66 deletions src/commands/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,14 @@ interface BuildOptions {
bin?: Record<string, { input: string; sourcemap?: boolean }>;
}

export const distDir = "dist";
export const DIST_DIR = "dist";

interface PackageInfo {
packagePath: string;
cwd: string;
pkg: any;
fullName: string;
}

export const buildCommand = createCommand<
{},
Expand All @@ -42,31 +49,48 @@ export const buildCommand = createCommand<
});
},
async handler(args) {
config.dists = config.dists || [
{
distDir: DIST_DIR,
distPath: ''
}
];
if (args.single) {
await buildSingle();
await buildSingle({ distDir: DIST_DIR });
return;
}

const limit = pLimit(4);
const packages = await globby("packages/**/package.json", {
cwd: process.cwd(),
absolute: true,
ignore: ["**/node_modules/**", `**/${distDir}/**`],
ignore: ["**/node_modules/**", ...config.dists.map(({ distDir }) => `**/${distDir}/**`)],
});

await Promise.all(
packages.map((packagePath) =>
limit(() => build(packagePath, config, reporter))
)
const packageInfoList: PackageInfo[] = await Promise.all(
packages.map(packagePath => limit(async () => {
const cwd = packagePath.replace("/package.json", "");
const pkg = await fs.readJSON(resolve(cwd, 'package.json'));
const fullName: string = pkg.name;
return { packagePath, cwd, pkg, fullName };
}))
);

for (const { distDir, distPath } of config.dists) {
await Promise.all(
packageInfoList.map(({ packagePath, cwd, pkg, fullName }) =>
limit(() => build({ packagePath, cwd, pkg, fullName, config, reporter, distDir, distPath, packageInfoList }))
)
);
}
},
};
});

async function buildSingle() {
async function buildSingle({ distDir, distPath = '' }: { distDir: string; distPath?: string; }) {
const cwd = process.cwd();
const packagePath = join(process.cwd(), "package.json");
const pkg = await readPackageJson(cwd);
const pkg = await fs.readJSON(packagePath);

validatePackageJson(pkg);

Expand All @@ -87,7 +111,7 @@ async function buildSingle() {
}),
typescript(),
generatePackageJson({
baseContents: rewritePackageJson(pkg),
baseContents: rewritePackageJson(pkg, distPath),
additionalDependencies: Object.keys(pkg.dependencies || {}),
}),
],
Expand All @@ -108,24 +132,16 @@ async function buildSingle() {
const generates = [
{
...commonOutputOptions,
file: join(distDir, "index.cjs.js"),
file: join(distDir, "index.js"),
format: "cjs" as const,
},
{
...commonOutputOptions,
file: join(distDir, "index.esm.js"),
file: join(distDir, "index.mjs"),
format: "esm" as const,
},
];

if (pkg.exports) {
generates.push({
...commonOutputOptions,
file: join(distDir, "index.mjs"),
format: "esm" as const,
});
}

await Promise.all(
generates.map(async (outputOptions) => {
await bundle.write(outputOptions);
Expand All @@ -135,26 +151,22 @@ async function buildSingle() {
// move README.md and LICENSE
await copyToDist(
cwd,
["README.md", "LICENSE"].concat(buildOptions?.copy || [])
["README.md", "LICENSE"].concat(buildOptions?.copy || []),
DIST_DIR + distPath
);
}

async function build(
packagePath: string,
config: BobConfig,
reporter: Consola
{ 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[] },
) {
const scope = config.scope;
const cwd = packagePath.replace("/package.json", "");
const pkg = await readPackageJson(cwd);
const fullName: string = pkg.name;

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

const name = fullName.replace(`${scope}/`, "");
const name = fullName.replace(`${scope}/`, distPath);

validatePackageJson(pkg);

Expand Down Expand Up @@ -184,7 +196,7 @@ async function build(
packageJSONPath: packagePath,
}),
generatePackageJson({
baseContents: rewritePackageJson(pkg),
baseContents: rewritePackageJson(pkg, distPath),
additionalDependencies: Object.keys(pkg.dependencies || {}),
}),
],
Expand All @@ -205,24 +217,16 @@ async function build(
const generates = [
{
...commonOutputOptions,
file: join(bobProjectDir, "index.cjs.js"),
file: join(bobProjectDir, "index.js"),
format: "cjs" as const,
},
{
...commonOutputOptions,
file: join(bobProjectDir, "index.esm.js"),
file: join(bobProjectDir, "index.mjs"),
format: "esm" as const,
},
];

if (pkg.exports) {
generates.push({
...commonOutputOptions,
file: join(distDir, "index.mjs"),
format: "esm" as const,
});
}

const declarations = await globby("**/*.d.ts", {
cwd: distProjectSrcDir,
ignore: ["**/node_modules/**"],
Expand Down Expand Up @@ -269,37 +273,45 @@ async function build(
banner: `#!/usr/bin/env node`,
preferConst: true,
sourcemap: options.sourcemap,
file: join(bobProjectDir, pkg.bin[alias].replace(`${distDir}/`, "")),
file: join(bobProjectDir, pkg.bin[alias].replace(`${DIST_DIR}/`, "")),
format: "cjs",
});
})
);
}

// remove <project>/dist
await fs.remove(join(cwd, distDir));
await fs.remove(join(cwd, DIST_DIR + distPath));

// fix distPath import in extra dists
function replaceAll(str: string, from: string, to: string) {
return str.split(from).join(to);
}
if (distPath) {
await Promise.all(
generates.map(({ file }) => limit(async () => {
let content = await fs.readFile(file, 'utf8');
for (const { fullName } of packageInfoList) {
content = replaceAll(content, `'${fullName}'`, `'${fullName}${distPath}'`);
}
await fs.writeFile(file, content, { encoding: 'utf8', flag: 'w' });
}))
)
}

// move bob/<project-name> to <project>/dist
await fs.move(bobProjectDir, join(cwd, distDir));
await fs.move(bobProjectDir, join(cwd, DIST_DIR + distPath));
// move README.md and LICENSE
await copyToDist(
cwd,
["README.md", "LICENSE"].concat(pkg.buildOptions?.copy || [])
["README.md", "LICENSE"].concat(pkg.buildOptions?.copy || []),
DIST_DIR + distPath
);

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

//

export async function readPackageJson(baseDir: string) {
return JSON.parse(
await fs.readFile(resolve(baseDir, "package.json"), {
encoding: "utf-8",
})
);
}

function rewritePackageJson(pkg: Record<string, any>) {
function rewritePackageJson(pkg: Record<string, any>, distPath: string) {
const newPkg: Record<string, any> = {};
const fields = [
"name",
Expand All @@ -323,18 +335,30 @@ function rewritePackageJson(pkg: Record<string, any>) {
}
});

newPkg.main = "index.cjs.js";
newPkg.module = "index.esm.js";
newPkg.name += distPath;
newPkg.main = "index.js";
newPkg.module = "index.mjs";
newPkg.typings = "index.d.ts";
newPkg.typescript = {
definition: newPkg.typings,
};

newPkg.exports = {
".": {
require: "./index.js",
import: "./index.mjs",
},
"./*": {
require: "./*.js",
import: "./*.mjs",
},
};

if (pkg.bin) {
newPkg.bin = {};

for (const alias in pkg.bin) {
newPkg.bin[alias] = pkg.bin[alias].replace(`${distDir}/`, "");
newPkg.bin[alias] = pkg.bin[alias].replace(`${DIST_DIR}/`, "");
}
}

Expand All @@ -352,18 +376,19 @@ export function validatePackageJson(pkg: any) {
}
}

expect("main", `${distDir}/index.cjs.js`);
expect("module", `${distDir}/index.esm.js`);
expect("typings", `${distDir}/index.d.ts`);
expect("typescript.definition", `${distDir}/index.d.ts`);
expect("main", `${DIST_DIR}/index.js`);
expect("module", `${DIST_DIR}/index.mjs`);
expect("typings", `${DIST_DIR}/index.d.ts`);
expect("typescript.definition", `${DIST_DIR}/index.d.ts`);

if (pkg.exports) {
expect("exports.require", `./${pkg.main}`);
expect("exports.default", `./${distDir}/index.mjs`);
}
expect("exports['.'].require", `./${DIST_DIR}/index.js`);
expect("exports['.'].import", `./${DIST_DIR}/index.mjs`);

expect("exports['./*'].require", `./${DIST_DIR}/*.js`);
expect("exports['./*'].import", `./${DIST_DIR}/*.mjs`);
}

async function copyToDist(cwd: string, files: string[]) {
async function copyToDist(cwd: string, files: string[], distDir: string) {
const allFiles = await globby(files, { cwd });

return Promise.all(
Expand Down
12 changes: 6 additions & 6 deletions src/commands/pack-flat.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import globby from "globby";
import pLimit from "p-limit";
import fs from "fs-extra";
import fs, { readJSON } from "fs-extra";
import { resolve, join } from "path";
import { execSync } from "child_process";
import { Consola } from "consola";
import { paramCase } from "param-case";

import { createCommand } from "../command";
import { BobConfig } from "../config";
import { readPackageJson, distDir } from "./build";
import { DIST_DIR } from "./build";

export const packFlatCommand = createCommand<
{},
Expand Down Expand Up @@ -36,7 +36,7 @@ export const packFlatCommand = createCommand<
const packages = await globby("packages/**/package.json", {
cwd: process.cwd(),
absolute: true,
ignore: ["**/node_modules/**", `**/${distDir}/**`],
ignore: ["**/node_modules/**", `**/${DIST_DIR}/**`],
});

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

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

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

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

// replace version to 0.0.0-canary-${commit}
const distPkg = await readPackageJson(projectDistDir);
const distPkg = await readJSON(join(projectDistDir, 'package.json'));
const version = `0.0.0-canary-${commit}`;
distPkg.version = version;
await fs.writeFile(join(projectDistDir, 'package.json'), JSON.stringify(distPkg, null, 2), {
Expand Down
4 changes: 4 additions & 0 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ export interface BobConfig {
commands?: {
[cmdName: string]: Command;
};
dists?: {
distDir: string;
distPath?: string;
}[]
Comment on lines +22 to +25
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need this or could we maybe have cjs, esm, nodeEsm flags? @ardatan

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's the story behind dists?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having es5 builds for graphql-tools
https://github.com/ardatan/graphql-tools/blob/master/bob.config.js#L9
We have needed that before in MSTeams

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh, so it's an output from tsc?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes it is actually. https://github.com/ardatan/graphql-tools/blob/master/package.json#L10
Here we run tsc twice in parallel, one is for modern es and the other one is for es5.

Copy link
Collaborator Author

@ardatan ardatan Jun 8, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure this old solution is the best but it worked for MSTeams :D It was a long time ago, we can change it for sure. But that was the reason behind having this dists thing.

Copy link
Owner

@kamilkisiela kamilkisiela Jun 8, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kamilkisiela @kamilkisiela (for me) add nodeEsm flag to the PR and merge it

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(also for me) update validation rules in bob validate to make sure it’s in sync

}

interface UseConfigOptions {
Expand Down