Skip to content

Commit fa70ff7

Browse files
feat: build esm modules using ipjs (#865)
This builds up on @achingbrain 's work on #863 with build improvements and full support This adds: - `ipjs` for ESM modules - auto-detected - `types` property transformation, allowing real time ts check in dev and path update for dist folder (TLDR no dist in the path) - `release` for ESM modules will navigate to the dist to publish its content - Dockerfile to bundlesize action per actions/runner#772 (comment) as we need node14+ for ESM One of the problematic modules in skypack using aegir is `uint8arrays`. It is a CJS module that depends on a ESM first module (multiformats), which makes skypack to get bad dependency paths. I tested this out shipping `uint8arrays` achingbrain/uint8arrays#22 PR and everything working smoothly 🎉 Original release: https://codepen.io/vascosantos/pen/KKmXoPV?editors=0011 Scoped release using `aegir`: https://codepen.io/vascosantos/pen/bGWoONq?editors=0011 (see browser built in console for errors) Co-authored-by: achingbrain <alex@achingbrain.net>
1 parent 2945fad commit fa70ff7

22 files changed

Lines changed: 236 additions & 15 deletions

File tree

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package-lock.json
22
yarn.lock
3-
node_modules
3+
/node_modules
4+
/actions/bundle-size/node_modules
45
/coverage
56
/dist
67
/docs

actions/bundle-size/.dockerignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
**
2+
!/dist

actions/bundle-size/Dockerfile

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Just enough docker until github gets a new node16 runner
2+
# see: https://github.com/actions/runner/issues/772
3+
FROM node:16-alpine
4+
WORKDIR /usr/src/app
5+
COPY dist/index.js .
6+
CMD [ "node", "index.js" ]

actions/bundle-size/action.yml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,9 @@ inputs:
88
description: A directory to run the bundle check in
99
required: false
1010
runs:
11-
using: 'node12'
12-
main: 'dist/index.js'
11+
# TODO: we need node14.14 minimum.
12+
# https://github.com/actions/runner/issues/772
13+
# using: 'node12'
14+
# main: 'dist/index.js'
15+
using: 'docker'
16+
image: 'Dockerfile'

md/esm.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# ESM support
2+
3+
## Setup
4+
5+
`aegir` leverages [ipjs](https://github.com/mikeal/ipjs) to output a build with `cjs` and `esm` for maximum compatibility. The general guidelines for writing a module in `esm` are detailed on the `ipjs` README. `aegir` will automatically identify a `esm` repo by the `module` property in `package.json`.
6+
7+
## Electron testing
8+
9+
Electron does [not support ESM](https://github.com/electron/electron/issues/21457) at the time of writing. When writing a module using ESM, we need to compile the tests to `cjs` and rely on them. For generating the build including the tests:
10+
11+
```bash
12+
aegir build --esm-tests
13+
```
14+
15+
## Lerna Monorepo
16+
17+
When using a lerna monorepo, local dependencies are symlinked by lerna on install. This means that an `esm` module will not use the resulting `dist` folder as symlink. This can become a problem if we are testing the `cjs` build of a module.
18+
19+
To work around the above problem, we can use `publishConfig.directory = "dist"` in `package.json` to notice lerna about the symlink path. After running the `aegir build` command, it is necessary to run `lerna link` to update the symlinks.
20+
21+
## Release
22+
23+
When releasing an `esm` module, the published content will be the generated `dist` folder content, as indicated by [ipjs](https://github.com/mikeal/ipjs).
24+
25+
## Examples
26+
27+
TODO: List examples when merged (`ipfs-unixfs`, `uint8arrays`)

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,13 +87,14 @@
8787
"esbuild-register": "^2.3.0",
8888
"eslint": "^7.23.0",
8989
"eslint-config-ipfs": "^2.0.0",
90-
"execa": "^5.0.0",
90+
"execa": "^5.1.1",
9191
"extract-zip": "^2.0.1",
9292
"fs-extra": "^10.0.0",
9393
"gh-pages": "^3.1.0",
9494
"git-authors-cli": "^1.0.33",
9595
"globby": "^11.0.3",
9696
"ipfs-utils": "^8.1.0",
97+
"ipjs": "^5.0.5",
9798
"it-glob": "~0.0.10",
9899
"kleur": "^4.1.4",
99100
"lilconfig": "^2.0.2",
@@ -119,7 +120,7 @@
119120
"typedoc": "^0.21.2",
120121
"typescript": "^4.3.5",
121122
"update-notifier": "^5.0.0",
122-
"yargs": "^17.0.1"
123+
"yargs": "^17.1.1"
123124
},
124125
"devDependencies": {
125126
"@types/bytes": "^3.1.0",

src/build/index.js

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,44 @@ const build = async (argv) => {
5656
return outfile
5757
}
5858

59+
/**
60+
* Build command
61+
*
62+
* @param {GlobalOptions & BuildOptions} argv
63+
*/
64+
const buildEsm = async (argv) => {
65+
const dist = path.join(process.cwd(), 'dist')
66+
// @ts-ignore no types
67+
const ipjs = await import('ipjs')
68+
69+
await ipjs.default({
70+
dist,
71+
onConsole: (/** @type {any[]} */...args) => console.info.apply(console, args),
72+
cwd: process.cwd(),
73+
main: argv.esmMain,
74+
tests: argv.esmTests
75+
})
76+
}
77+
5978
const tasks = new Listr([
6079
{
6180
title: 'Clean ./dist',
6281
task: async () => del(path.join(process.cwd(), 'dist'))
6382
},
83+
{
84+
title: 'Build ESM',
85+
enabled: ctx => {
86+
return pkg.type === 'module'
87+
},
88+
/**
89+
*
90+
* @param {GlobalOptions & BuildOptions} ctx
91+
* @param {Task} task
92+
*/
93+
task: async (ctx, task) => {
94+
await buildEsm(ctx)
95+
}
96+
},
6497
{
6598
title: 'Bundle',
6699
enabled: ctx => ctx.bundle,

src/cmds/build.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,18 @@ module.exports = {
3737
type: 'boolean',
3838
describe: 'Build the Typescripts type declarations.',
3939
default: userConfig.build.types
40+
},
41+
esmMain: {
42+
alias: 'esm-main',
43+
type: 'boolean',
44+
describe: 'Include a main field in a built esm project',
45+
default: userConfig.build.esmMain
46+
},
47+
esmTests: {
48+
alias: 'esm-tests',
49+
type: 'boolean',
50+
describe: 'Include tests in a built esm project',
51+
default: userConfig.build.esmTests
4052
}
4153
})
4254
},

src/config/user.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,9 @@ const defaults = {
4040
bundlesize: false,
4141
bundlesizeMax: '100kB',
4242
types: true,
43-
config: {}
43+
config: {},
44+
esmMain: true,
45+
esmTests: false
4446
},
4547
// linter cmd options
4648
lint: {

src/release/publish.js

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
'use strict'
22

33
const execa = require('execa')
4-
const { otp } = require('../utils')
4+
const { otp, pkg, repoDirectory } = require('../utils')
55
/**
66
* @typedef {import('./../types').ReleaseOptions} ReleaseOptions
77
* @typedef {import('listr').ListrTaskWrapper} ListrTask
@@ -25,16 +25,22 @@ function publish (ctx, task) {
2525
task.title += ` (npm ${publishArgs.join(' ')})`
2626
}
2727

28-
return execa('npm', publishArgs)
29-
.catch(async (error) => {
30-
if (error.toString().includes('provide a one-time password')) {
31-
const code = await otp()
32-
task.title += '. Trying again with OTP.'
33-
return await execa('npm', publishArgs.concat('--otp', code))
28+
// Publish from dist if ESM
29+
const execaOptions = pkg.type === 'module'
30+
? {
31+
cwd: `${repoDirectory}/dist`
3432
}
33+
: {}
3534

36-
throw error
37-
})
35+
return execa('npm', publishArgs, execaOptions).catch(async (error) => {
36+
if (error.toString().includes('provide a one-time password')) {
37+
const code = await otp()
38+
task.title += '. Trying again with OTP.'
39+
return await execa('npm', publishArgs.concat('--otp', code), execaOptions)
40+
}
41+
42+
throw error
43+
})
3844
}
3945

4046
module.exports = publish

0 commit comments

Comments
 (0)