Skip to content

Commit

Permalink
feat: support pnpm workspaces (#160)
Browse files Browse the repository at this point in the history
Co-authored-by: Laurin Quast <laurinquast@googlemail.com>
  • Loading branch information
dimaMachina and n1ru4l committed Dec 22, 2022
1 parent e47642a commit 9ce6e27
Show file tree
Hide file tree
Showing 26 changed files with 559 additions and 410 deletions.
8 changes: 8 additions & 0 deletions .changeset/rotten-buses-clean.md
@@ -0,0 +1,8 @@
---
"bob-the-bundler": minor
---

Support pnpm workspaces from `pnpm-workspace.yaml`..
Throw an error in case both `pnpm-workspace.yaml` and `package.json#workspaces` fields exist.
Add missing dependency `execa`.
Cleanup and remove unused dependencies.
10 changes: 4 additions & 6 deletions README.md
Expand Up @@ -24,7 +24,7 @@ You can add a `bob` key to each `package.json`.

**Disable bob for a single package**

```js
```jsonc
{
"name": "graphql-lfg",
"bob": false // exclude a single package from all things bob related
Expand All @@ -33,7 +33,7 @@ You can add a `bob` key to each `package.json`.

**Disable build for a single package**

```js
```json
{
"name": "graphql-lfg",
"bob": {
Expand All @@ -44,7 +44,7 @@ You can add a `bob` key to each `package.json`.

**Disable check for a single package**

```js
```json
{
"name": "graphql-lfg",
"bob": {
Expand All @@ -55,7 +55,7 @@ You can add a `bob` key to each `package.json`.

**Disable check for a single export in a package**

```js
```json
{
"name": "graphql-lfg",
"bob": {
Expand All @@ -69,11 +69,9 @@ You can add a `bob` key to each `package.json`.
## Usage

```bash

$ bob build
$ bob check

# only use this command if you know the secret sauce
$ bob runify

```
16 changes: 4 additions & 12 deletions package.json
Expand Up @@ -22,24 +22,17 @@
"jest-resolver.js"
],
"dependencies": {
"@rollup/plugin-json": "^6.0.0",
"@rollup/plugin-node-resolve": "^13.3.0",
"@vercel/ncc": "^0.36.0",
"builtins": "^5.0.1",
"consola": "^2.15.3",
"cross-spawn": "^7.0.3",
"dependency-graph": "^0.11.0",
"execa": "5.1.1",
"fs-extra": "^10.1.0",
"globby": "^11.0.0",
"js-yaml": "^4.1.0",
"lodash.get": "^4.4.2",
"minimatch": "^5.1.0",
"mkdirp": "^1.0.4",
"p-limit": "^3.1.0",
"param-case": "^3.0.4",
"resolve.exports": "^1.1.0",
"rollup": "^2.75.6",
"rollup-plugin-generate-package-json": "^3.2.0",
"rollup-plugin-typescript2": "^0.34.0",
"tslib": "^2.0.0",
"tsup": "^6.5.0",
"yargs": "^17.5.1",
Expand All @@ -55,18 +48,17 @@
},
"devDependencies": {
"@actions/core": "1.9.1",
"@changesets/cli": "2.24.4",
"@changesets/changelog-github": "0.4.6",
"@changesets/cli": "2.24.4",
"@jest/types": "28.1.3",
"@types/cross-spawn": "6.0.2",
"@types/fs-extra": "9.0.13",
"@types/jest": "28.1.8",
"@types/js-yaml": "4.0.5",
"@types/lodash.get": "4.4.7",
"@types/minimatch": "3.0.5",
"@types/mkdirp": "1.0.2",
"@types/node": "16.11.58",
"@types/yargs": "15.0.14",
"execa": "5.1.1",
"jest": "28.1.3",
"rimraf": "3.0.2",
"ts-jest": "28.0.8",
Expand Down
16 changes: 6 additions & 10 deletions src/commands/bootstrap.ts
Expand Up @@ -70,7 +70,7 @@ export const presetFieldsESM = {
},
};

async function applyESMModuleTransform(cwd: string) {
async function applyESMModuleTransform(cwd = process.cwd()) {
const filePaths = await globby("**/*.ts", {
cwd,
absolute: true,
Expand Down Expand Up @@ -108,26 +108,22 @@ export const bootstrapCommand = createCommand<{}, {}>(() => {
return yargs.options({});
},
async handler() {
const cwd = process.cwd();
const rootPackageJSON = await getRootPackageJSON(cwd);
const workspaces = getWorkspaces(rootPackageJSON);
const rootPackageJSON = await getRootPackageJSON();
const workspaces = await getWorkspaces(rootPackageJSON);
const isSinglePackage = workspaces === null;

// Make sure all modules are converted to ESM

if (isSinglePackage) {
await applyESMModuleTransform(cwd);
await applyESMModuleTransform();
await applyPackageJSONPresetConfig(
path.join(cwd, "package.json"),
path.join(process.cwd(), "package.json"),
rootPackageJSON
);
return;
}

const workspacePackagePaths = await getWorkspacePackagePaths(
cwd,
workspaces
);
const workspacePackagePaths = await getWorkspacePackagePaths(workspaces);

await Promise.all(
workspacePackagePaths.map((packagePath) =>
Expand Down
32 changes: 13 additions & 19 deletions src/commands/build.ts
Expand Up @@ -26,7 +26,7 @@ interface PackageInfo {
}

/**
* A list of files that we don't need need within the published package.
* A list of files that we don't need within the published package.
* Also known as test files :)
* This list is derived from scouting various of our repositories.
*/
Expand Down Expand Up @@ -56,17 +56,14 @@ function typeScriptCompilerOptions(
};
}

function compilerOptionsToArgs(
options: Record<string, unknown>
): Array<string> {
const args: Array<string> = [];
for (const [key, value] of Object.entries(options)) {
args.push(`--${key}`, `${value}`);
}
return args;
function compilerOptionsToArgs(options: Record<string, unknown>): string[] {
return Object.entries(options).flatMap(([key, value]) => [
`--${key}`,
`${value}`,
]);
}

function assertTypeScriptBuildResult(result: execa.ExecaReturnValue<string>) {
function assertTypeScriptBuildResult(result: execa.ExecaReturnValue) {
if (result.exitCode !== 0) {
console.log("TypeScript compiler exited with non-zero exit code.");
console.log(result.stdout);
Expand Down Expand Up @@ -121,8 +118,8 @@ export const buildCommand = createCommand<
},
async handler({ incremental }) {
const cwd = process.cwd();
const rootPackageJSON = await getRootPackageJSON(cwd);
const workspaces = getWorkspaces(rootPackageJSON);
const rootPackageJSON = await getRootPackageJSON();
const workspaces = await getWorkspaces(rootPackageJSON);
const isSinglePackage = workspaces === null;

if (isSinglePackage) {
Expand Down Expand Up @@ -151,10 +148,7 @@ export const buildCommand = createCommand<
}

const limit = pLimit(4);
const workspacePackagePaths = await getWorkspacePackagePaths(
cwd,
workspaces
);
const workspacePackagePaths = await getWorkspacePackagePaths(workspaces);

const packageInfoList: PackageInfo[] = await Promise.all(
workspacePackagePaths.map((packagePath) =>
Expand Down Expand Up @@ -392,7 +386,7 @@ function rewritePackageJson(pkg: Record<string, any>, typesOnly: boolean) {
];

fields.forEach((field) => {
if (typeof pkg[field] !== "undefined") {
if (pkg[field] !== undefined) {
newPkg[field] = pkg[field];
}
});
Expand Down Expand Up @@ -461,7 +455,7 @@ export function validatePackageJson(
// If the package has NO binary we need to check the exports map.
// a package should either
// 1. have a bin property
// 2. have a exports property
// 2. have an exports property
// 3. have an exports and bin property
if (Object.keys(pkg.bin ?? {}).length > 0) {
if (opts.includesCommonJS === true) {
Expand Down Expand Up @@ -499,7 +493,7 @@ export function validatePackageJson(
expect("typings", presetFieldsESM.typings);
expect("typescript.definition", presetFieldsESM.typescript.definition);

// For now we enforce a top level exports property
// For now, we enforce a top level exports property
expect("exports['.']", presetFieldsESM.exports["."]);
}
}
Expand Down
6 changes: 3 additions & 3 deletions src/commands/check.ts
Expand Up @@ -40,8 +40,8 @@ export const checkCommand = createCommand<{}, {}>((api) => {
},
async handler() {
const cwd = process.cwd();
const rootPackageJSON = await getRootPackageJSON(cwd);
const workspaces = getWorkspaces(rootPackageJSON);
const rootPackageJSON = await getRootPackageJSON();
const workspaces = await getWorkspaces(rootPackageJSON);
const isSinglePackage = workspaces === null;

let checkConfigs: Array<{
Expand All @@ -55,7 +55,7 @@ export const checkCommand = createCommand<{}, {}>((api) => {
packageJSON: rootPackageJSON,
});
} else {
const workspacesPaths = await getWorkspacePackagePaths(cwd, workspaces);
const workspacesPaths = await getWorkspacePackagePaths(workspaces);
const limit = pLimit(20);
await Promise.all(
workspacesPaths.map((workspacePath) =>
Expand Down
33 changes: 11 additions & 22 deletions src/commands/runify.ts
Expand Up @@ -287,7 +287,6 @@ async function compile(
}
: {},
});

return;
}

Expand All @@ -297,25 +296,15 @@ async function compile(
});

await fs.mkdirp(join(cwd, "dist"));
await Promise.all(
[
fs.writeFile(join(cwd, "dist/index.js"), code, {
encoding: "utf-8",
}),
fs.writeFile(join(cwd, "dist/index.js.map"), map, {
encoding: "utf-8",
}),
].concat(
Object.keys(assets).map(async (filepath) => {
if (filepath.endsWith("package.json")) {
return Promise.resolve();
}
await fs.ensureDir(dirname(join(cwd, "dist", filepath)), {});
await fs.writeFile(
join(cwd, "dist", filepath),
assets[filepath].source
);
})
)
);
await Promise.all([
fs.writeFile(join(cwd, "dist/index.js"), code, "utf8"),
fs.writeFile(join(cwd, "dist/index.js.map"), map, "utf8"),
...Object.keys(assets).map(async (filepath) => {
if (filepath.endsWith("package.json")) {
return;
}
await fs.ensureDir(dirname(join(cwd, "dist", filepath)), {});
await fs.writeFile(join(cwd, "dist", filepath), assets[filepath].source);
}),
]);
}
3 changes: 0 additions & 3 deletions src/typings.d.ts
@@ -1,4 +1 @@
declare module 'rollup-plugin-generate-package-json';
declare module 'rollup-plugin-auto-external';
declare module 'builtins';
declare module '@vercel/ncc';
2 changes: 1 addition & 1 deletion src/utils/get-root-package-json.ts
@@ -1,7 +1,7 @@
import globby from "globby";
import * as fse from "fs-extra";

export async function getRootPackageJSON(cwd: string) {
export async function getRootPackageJSON(cwd = process.cwd()) {
const [rootPackageJSONPath] = await globby("package.json", {
cwd,
absolute: true,
Expand Down
4 changes: 2 additions & 2 deletions src/utils/get-workspace-package-paths.ts
Expand Up @@ -4,8 +4,8 @@ import path from "path";
import { buildArtifactDirectories } from "../constants";

export async function getWorkspacePackagePaths(
cwd: string,
workspaces: Array<string>
workspaces: string[],
cwd = process.cwd()
) {
const packageJSONPaths = await globby(
workspaces
Expand Down
24 changes: 21 additions & 3 deletions src/utils/get-workspaces.ts
@@ -1,4 +1,7 @@
import path from "node:path";
import zod from "zod";
import fse from "fs-extra";
import jsYaml from "js-yaml";

const WorkspaceModel = zod.optional(
zod.union([
Expand All @@ -10,10 +13,25 @@ const WorkspaceModel = zod.optional(
])
);

export function getWorkspaces(
export async function getWorkspaces(
packageJSON: Record<string, unknown>
): Array<string> | null {
const result = WorkspaceModel.parse(packageJSON.workspaces);
): Promise<string[] | null> {
let result = WorkspaceModel.parse(packageJSON.workspaces);

const pnpmWorkspacePath = path.join(process.cwd(), "pnpm-workspace.yaml");
const isPnpmWorkspace = await fse.pathExists(pnpmWorkspacePath);

if (isPnpmWorkspace) {
if (result) {
throw new Error(
"Both `pnpm-workspace.yaml` and `package.json#workspaces` are not supported. Remove `package.json#workspaces` field."
);
}

result = jsYaml.load(await fse.readFile(pnpmWorkspacePath, "utf8")) as {
packages?: string[];
};
}
if (result == null) {
return null;
}
Expand Down
4 changes: 2 additions & 2 deletions src/utils/rewrite-code-imports.ts
Expand Up @@ -4,13 +4,13 @@ import * as path from "path";
function isFolderSync(path: string) {
try {
return fse.statSync(path).isDirectory();
} catch (e) {
} catch {
return false;
}
}

function rewriteSourceValue(sourceValue: string, relativeDirname: string) {
if (sourceValue.startsWith(".") && sourceValue.endsWith(".js") === false) {
if (sourceValue.startsWith(".") && !sourceValue.endsWith(".js")) {
const targetPath = path.resolve(relativeDirname, sourceValue);
// If the target path is a folder, we need to import from the index.js file
if (isFolderSync(targetPath)) {
Expand Down

0 comments on commit 9ce6e27

Please sign in to comment.