Skip to content

Commit

Permalink
modular build <library> (#195)
Browse files Browse the repository at this point in the history
* This PR adds a feature to let modular build workspaces as libraries (as opposed to apps). It uses rollup to generate commonjs and esm builds of the workspace it's pointing to (based on the `main` field in the workspace's `package.json`), and then generates typescript typings for the same. It then generates a publishable folder under `dist/<workspace name>`, with a modified `package.json` that points to these built versions of the files. The folder will also contain any other files specified by the `files` field, and/or `.npmignore` in the workspace.

This is a port of the script we used internally at JPM for the same, and makes it a first class feature for modular. It's fast and works fairly well.

* optionally preserve module structure, better messaging, tests, miscellaneous

* parallel builds, and move app builds to /dist/<app-name>

* puppeteer is now in optionalDependencies, and we don't pass globals to rollup's globals anymore.

* prettier files

* order extensions

* lazily import the build script to defer top level side effects

* make sure build is run from a modular project root

Co-authored-by: Sunil Pai <sunil.pai@jpmorgan.com>
Co-authored-by: Luke Sheard <LukeSheard@users.noreply.github.com>
Co-authored-by: Luke Sheard <luke.sheard@jpmorgan.com>
  • Loading branch information
4 people authored Dec 23, 2020
1 parent 8fcd4ee commit 33649d2
Show file tree
Hide file tree
Showing 9 changed files with 1,228 additions and 56 deletions.
5 changes: 5 additions & 0 deletions .changeset/tender-rice-whisper.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'modular-scripts': minor
---

modular build `<library>`
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,9 @@ packages/view-2
npm-debug.log*
yarn-debug.log*
yarn-error.log*

/dist

# Used by typescript for incremental builds
.tsbuildinfo
*.tsbuildinfo
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,5 +64,8 @@
"lint-staged": {
"*.{js,ts,tsx}": "eslint --cache --fix --ext .js,.ts,.tsx --max-warnings 0",
"*.{js,json,ts,tsx,css,md,mdx}": "prettier --write"
},
"resolutions": {
"rollup": "^2.33.3"
}
}
17 changes: 15 additions & 2 deletions packages/modular-scripts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,13 @@
"build": "babel --root-mode upward src --out-dir build --extensions .ts --ignore 'src/**/*.test.ts'"
},
"dependencies": {
"@babel/plugin-proposal-class-properties": "^7.12.1",
"@babel/preset-env": "^7.12.10",
"@craco/craco": "^5.8.0",
"@rollup/plugin-babel": "^5.2.1",
"@rollup/plugin-commonjs": "^16.0.0",
"@rollup/plugin-json": "^4.1.0",
"builtin-modules": "^3.1.0",
"chalk": "^4.1.0",
"change-case": "^4.1.1",
"codegen.macro": "^4.0.0",
Expand All @@ -26,18 +32,25 @@
"prompts": "^2.4.0",
"react-scripts": "^4.0.1",
"resolve-as-bin": "^2.1.0",
"rimraf": "^3.0.2"
"rimraf": "^3.0.2",
"rollup": "^2.33.3",
"rollup-plugin-postcss": "^3.1.8",
"tar": "^6.0.5"
},
"devDependencies": {
"@schemastore/package": "^0.0.6",
"@schemastore/tsconfig": "^0.0.9",
"@types/find-up": "^4.0.0",
"@types/fs-extra": "^5.0.2",
"@types/mri": "^1.1.0",
"@types/node": "*",
"@types/prompts": "^2.0.9",
"@types/puppeteer": "^5.4.0",
"@types/rimraf": "^3.0.0",
"pptr-testing-library": "^0.6.4",
"@types/tar": "^4.0.4",
"pptr-testing-library": "^0.6.4"
},
"optionalDependencies": {
"puppeteer": "^5.4.1"
}
}
88 changes: 73 additions & 15 deletions packages/modular-scripts/src/__tests__/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import execa from 'execa';
import rimraf from 'rimraf';
import puppeteer from 'puppeteer';
import tree from 'tree-view-for-tests';
import path from 'path';
import fs from 'fs-extra';
Expand Down Expand Up @@ -28,17 +27,6 @@ jest.setTimeout(10 * 60 * 1000);

const packagesPath = path.join(getModularRoot(), 'packages');

function getBrowser() {
return puppeteer.launch(
process.env.CI
? {
headless: true,
args: ['--no-sandbox', '--disable-setuid-sandbox'],
}
: {},
);
}

function modular(str: string, opts: Record<string, unknown> = {}) {
return execa('yarnpkg', ['modular', ...str.split(' ')], {
cwd: modularRoot,
Expand Down Expand Up @@ -148,6 +136,7 @@ afterAll(() => {
rimraf.sync(path.join(packagesPath, 'sample-app'));
rimraf.sync(path.join(packagesPath, 'sample-view'));
rimraf.sync(path.join(packagesPath, 'sample-package'));
rimraf.sync(path.join(modularRoot, 'dist'));
// run yarn so yarn.lock gets reset
return execa.sync('yarnpkg', [], {
cwd: modularRoot,
Expand Down Expand Up @@ -189,11 +178,29 @@ describe('modular-scripts', () => {
});

it('can start an app', async () => {
// this leaves habing processes on local environments
// so we're disabling it for now. Still runs on CI though.
// Ok, so. Sunil's decided to get the new M1 MacBook Air. Some software doesn't run on it
// well yet. Particularly the puppeteer npm package failes to install and run
// (see https://github.com/puppeteer/puppeteer/issues/, issues #6634 and #6641,
// possible fix in pull #6495)

// Because of this, he's marked puppeteer in optionalDependencies, so it's failure to install
// doesn't block everything else. Further, because this particular test is already flaky,
// it's disabled when running locally. However, because it fails to install, it causes
// typescript and eslint failures. Hence the need to disable those errors for now.

// It's Sunil's responsibility to fix this when better, so shout at him if he doesn't.

/* eslint-disable @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-var-requires, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call */

// This seems to be leaving hanging processes locally,
// so marking this test as a no-op for now. Sigh.
if (!process.env.CI) {
return;
}

const puppeteer = require('puppeteer');

// @ts-expect-error FIXME
let browser: puppeteer.Browser | undefined;
let devServer: DevServer | undefined;
try {
Expand All @@ -202,7 +209,14 @@ describe('modular-scripts', () => {
path.join(packagesPath, 'sample-app', 'src', 'App.tsx'),
);

browser = await getBrowser();
browser = await puppeteer.launch(
process.env.CI
? {
headless: true,
args: ['--no-sandbox', '--disable-setuid-sandbox'],
}
: {},
);
devServer = await startApp('sample-app');

const page = await browser.newPage();
Expand All @@ -229,6 +243,8 @@ describe('modular-scripts', () => {
devServer.kill();
}
}

/* eslint-enable @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-var-requires, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call */
});

it('can add a view', async () => {
Expand Down Expand Up @@ -290,4 +306,46 @@ describe('modular-scripts', () => {
'PASS packages/sample-package/src/__tests__/index.test.ts',
);
});

it('can build libraries', async () => {
// cleanup anything built previously
rimraf.sync(path.join(modularRoot, 'dist'));

// build a view
await modular('build sample-view', { stdio: 'inherit' });
// build a package too, but preserve modules
await modular('build sample-package --preserve-modules', {
stdio: 'inherit',
});

expect(tree(path.join(modularRoot, 'dist'))).toMatchInlineSnapshot(`
"dist
├─ sample-package
│ ├─ README.md #1jv3l2q
│ ├─ dist
│ │ ├─ cjs
│ │ │ └─ src
│ │ │ ├─ index.js #rq9uxe
│ │ │ └─ index.js.map #95g4ej
│ │ ├─ es
│ │ │ └─ src
│ │ │ ├─ index.js #1gjntzw
│ │ │ └─ index.js.map #1861m7m
│ │ └─ types
│ │ └─ src
│ │ └─ index.d.ts #f68aj
│ └─ package.json
└─ sample-view
├─ README.md #11adaka
├─ dist
│ ├─ sample-view.cjs.js #fmbogr
│ ├─ sample-view.cjs.js.map #4xu206
│ ├─ sample-view.es.js #10hnw4k
│ ├─ sample-view.es.js.map #jqhhy5
│ └─ types
│ └─ src
│ └─ index.d.ts #1vloh7q
└─ package.json"
`);
});
});
Loading

0 comments on commit 33649d2

Please sign in to comment.