Skip to content

Commit

Permalink
feat: remove rollup application compiler, delegate compilation to the…
Browse files Browse the repository at this point in the history
… shell (#187)

* Support named imports
* Support ES modules treeshaking
* Support static file imports
* Support static files in a public directory.
* **Significantly** improve compilation performance
* Avoid out-of-memory issues when compiling large applications
* Automatically update cached shell when `@dhis2/cli-app-scripts` is upgraded

BREAKING CHANGE: This may break some applications which use the former named import workaround, but removing that workaround should make treeshaking work!!
  • Loading branch information
amcgee authored Nov 28, 2019
1 parent 8ba9ec7 commit cae7a07
Show file tree
Hide file tree
Showing 39 changed files with 1,527 additions and 14,894 deletions.
34 changes: 29 additions & 5 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,8 +1,32 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
node_modules
build
dist
yarn-error.log
.d2
.pnp
.pnp.js
.vscode

# testing
**/coverage

# production
**/build
**/dist

# DHIS2
.d2
**/src/locales
cli/assets

# Editors
.vscode

# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local

npm-debug.log*
yarn-debug.log*
yarn-error.log*
1 change: 0 additions & 1 deletion cli/.gitignore

This file was deleted.

2 changes: 1 addition & 1 deletion cli/config/.browserlistrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@ module.exports = [
'> 0.5%',
'last 2 versions',
'Firefox ESR',
'ie 11', // TODO: Remove?
'not ie 11',
'not dead',
]
8 changes: 8 additions & 0 deletions cli/config/app.babel.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module.exports = {
plugins: [
require('@babel/plugin-proposal-class-properties'),

// Always build in "production" mode even when styled-jsx runtime may select "development"
[require('styled-jsx/babel'), { optimizeForSpeed: true }],
],
}
5 changes: 4 additions & 1 deletion cli/config/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ module.exports = {
'^.+\\.[t|j]sx?$': require.resolve('./jest.transform.js'),
},
moduleNameMapper: {
'^.+\\.(css|less)$': require.resolve('./jest.identity.mock.js'),
'\\.(css|less)$': require.resolve('./jest.identity.mock.js'),
'\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': require.resolve(
'./jest.file.mock.js'
),
},
}
1 change: 1 addition & 0 deletions cli/config/jest.file.mock.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = 'test-file-stub'
5 changes: 3 additions & 2 deletions cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"@dhis2/ui-core": "^3.11.0",
"archiver": "^3.1.1",
"babel-jest": "^24.9.0",
"chokidar": "^3.3.0",
"classnames": "^2.2.6",
"detect-port": "^1.3.0",
"dotenv": "^8.1.0",
Expand Down Expand Up @@ -59,8 +60,8 @@
"d2-app-scripts": "./bin/d2-app-scripts"
},
"scripts": {
"assets:prep": "rimraf assets && mkdir -p assets",
"assets:copy": "copyfiles ../shell/* \"../shell/**/*\" assets/shell --exclude node_modules --exclude .pnp --exclude .pnp.js",
"assets:prep": "rimraf assets && mkdirp assets",
"assets:copy": "copyfiles ../shell/* \"../shell/**/*\" assets/shell --all --exclude \"../shell/**/node_modules/**\" --exclude \"../shell/**/.pnp/**\" --exclude \"../shell/**/.pnp.js\"",
"assets:clean": "cd assets/shell && rimraf node_modules .pnp.js .pnp adapter/node_modules adapter/.pnp.js adapter/.pnp",
"build": "yarn assets:prep && yarn assets:copy",
"prepublishOnly": "yarn build"
Expand Down
13 changes: 7 additions & 6 deletions cli/src/commands/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ const fs = require('fs-extra')
const path = require('path')

const i18n = require('../lib/i18n')
const compile = require('../lib/compile')
const { compile } = require('../lib/compiler')
const makePaths = require('../lib/paths')
const makeShell = require('../lib/shell')
const parseConfig = require('../lib/parseConfig')
Expand All @@ -27,7 +27,7 @@ const getNodeEnv = () => {
}

const printBuildParam = (key, value) => {
reporter.print(chalk.green(` - ${key} =`), chalk.yellow(value))
reporter.print(chalk.green(` - ${key} :`), chalk.yellow(value))
}
const setAppParameters = (standalone, config) => {
process.env.PUBLIC_URL = process.env.PUBLIC_URL || '.'
Expand Down Expand Up @@ -59,7 +59,8 @@ const handler = async ({
mode = mode || (dev && 'development') || getNodeEnv() || 'production'
loadEnvFiles(paths, mode)

printBuildParam('Build Mode', mode)
reporter.print(chalk.green.bold('Build parameters:'))
printBuildParam('Mode', mode)

const config = parseConfig(paths)
const shell = makeShell({ config, paths })
Expand All @@ -85,19 +86,19 @@ const handler = async ({
await shell.bootstrap({ shell: shellSource, force })
}

reporter.info(`Building app ${chalk.bold(config.name)}...`)
reporter.info(
`Building ${config.type} ${chalk.bold(config.name)}...`
)
await compile({
config,
paths,
mode,
watch,
})
reporter.info(chalk.dim(` - Built in mode ${chalk.bold(mode)}`))

if (config.type === 'app') {
reporter.info('Building appShell...')
await shell.build()
reporter.info(chalk.dim(` - Built in mode ${chalk.bold(mode)}`))
}
},
{
Expand Down
2 changes: 1 addition & 1 deletion cli/src/commands/start.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ const { reporter, chalk } = require('@dhis2/cli-helpers-engine')
const detectPort = require('detect-port')

const i18n = require('../lib/i18n')
const compile = require('../lib/compile')
const { compile } = require('../lib/compiler')
const makePaths = require('../lib/paths')
const makeShell = require('../lib/shell')
const parseConfig = require('../lib/parseConfig')
Expand Down
17 changes: 17 additions & 0 deletions cli/src/lib/compiler/compile.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
const compileLibrary = require('./compileLibrary')
const compileApp = require('./compileApp')

const compile = async ({
config,
paths,
mode = 'development',
watch = false,
} = {}) => {
if (config.type === 'lib') {
return await compileLibrary({ config, paths, mode, watch })
} else {
return await compileApp({ config, paths, mode, watch })
}
}

module.exports = compile
123 changes: 123 additions & 0 deletions cli/src/lib/compiler/compileApp.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
const { reporter, chalk } = require('@dhis2/cli-helpers-engine')

const path = require('path')
const fs = require('fs-extra')
const chokidar = require('chokidar')
const babel = require('@babel/core')

const babelOptions = require('../../../config/app.babel.config')

const overwriteEntrypoint = async ({ config, paths }) => {
const shellAppSource = await fs.readFile(paths.shellSourceEntrypoint)

const entrypoint = config.entryPoints.app
if (!entrypoint.match(/^(\.\/)?src\//)) {
const msg = `App entrypoint ${chalk.bold(
entrypoint
)} must be located within the ${chalk.bold('./src')} directory`
reporter.error(msg)
throw new Error(msg)
}

const relativeEntrypoint = entrypoint.replace(/^(\.\/)?src\//, '')

try {
require.resolve(path.join(paths.base, entrypoint))
} catch (e) {
const msg = `Could not resolve app entrypoint ${chalk.bold(entrypoint)}`
reporter.error(msg)
throw new Error(msg)
}

await fs.writeFile(
paths.shellAppEntrypoint,
shellAppSource
.toString()
.replace(/'.\/D2App\/app'/g, `'./D2App/${relativeEntrypoint}'`)
)
}

const watchFiles = ({ inputDir, outputDir, processFileCallback, watch }) => {
const compileFile = async source => {
const relative = path.relative(inputDir, source)
const destination = path.join(outputDir, relative)
reporter.debug(
`File ${relative} changed or added... dest: `,
path.relative(inputDir, relative)
)
await fs.ensureDir(path.dirname(destination))
await processFileCallback(source, destination)
}

const removeFile = async file => {
const relative = path.relative(inputDir, file)
const outFile = path.join(outputDir, relative)
reporter.debug(`File ${relative} removed... removing: `, outFile)
fs.remove(outFile)
}

return new Promise((resolve, reject) => {
const watcher = chokidar.watch(inputDir, { persistent: true })

watcher
.on('ready', async () => {
if (watch) {
reporter.debug('watching...')
} else {
await watcher.close()
}
resolve()
})
.on('add', compileFile)
.on('change', compileFile)
.on('unlink', removeFile)
.on('error', error => {
reporter.debugError('Chokidar error:', error)
reject('Chokidar error!')
})

process.on('SIGINT', async () => {
reporter.debug('Caught interrupt signal')
await watcher.close()
})
})
}

const compileApp = async ({ config, paths, mode, watch }) => {
await overwriteEntrypoint({ config, paths })

fs.removeSync(paths.shellApp)
fs.ensureDirSync(paths.shellApp)

fs.removeSync(paths.shellPublic)
fs.copySync(paths.shellSourcePublic, paths.shellPublic)

const copyFile = async (source, destination) => {
await fs.copy(source, destination)
}
const compileFile = async (source, destination) => {
if (path.extname(source) === '.js') {
const result = await babel.transformFileAsync(source, babelOptions)
await fs.writeFile(destination, result.code)
} else {
await copyFile(source, destination)
}
}

return Promise.all([
watchFiles({
inputDir: paths.src,
outputDir: paths.shellApp,
processFileCallback: compileFile,
watch,
}),
watchFiles({
inputDir: paths.public,
outputDir: paths.shellPublic,
processFileCallback: copyFile,
watch,
}),
])
}

module.exports = compileApp
42 changes: 8 additions & 34 deletions cli/src/lib/compile.js → cli/src/lib/compiler/compileLibrary.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
const { reporter, chalk } = require('@dhis2/cli-helpers-engine')

const path = require('path')
const fs = require('fs-extra')
const rollup = require('rollup')
const fs = require('fs-extra')

const rollupConfigBuilder = require('../../config/rollup.config')
const rollupConfigBuilder = require('../../../config/rollup.config')

const printRollupError = error => {
reporter.debug('Rollup error', error)
Expand All @@ -21,12 +20,7 @@ const printRollupError = error => {
}
}

const compile = async ({
config,
paths,
mode = 'development',
watch = false,
} = {}) => {
const compileLibrary = async ({ config, paths, mode, watch }) => {
const input =
(config.entryPoints && config.entryPoints[config.type]) ||
'src/index.js'
Expand All @@ -43,32 +37,18 @@ const compile = async ({
entryPoint: path.join(paths.base, input),
outDir,
mode,
bundleDeps: config.type === 'app',
bundleDeps: false,
pkg,
})

const outFile = path.join(outDir, `es/${config.type}.js`)

reporter.print(chalk.green(' - Entrypoint :'), chalk.yellow(input))
reporter.print(
chalk.dim(
`Compiling ${chalk.bold(input)} to ${chalk.bold(
path.relative(process.cwd(), outFile)
)}`
)
chalk.green(' - Output Directory :'),
chalk.yellow(path.relative(paths.base, outDir))
)

reporter.debug('Rollup config', rollupConfig)

const copyOutput = async () => {
await fs.copy(outFile, path.join(paths.shellApp, `${config.type}.js`))
if (mode === 'production') {
await fs.copy(
outFile + '.map',
path.join(paths.shellApp, `${config.type}.js.map`)
)
}
}

if (!watch) {
// create a bundle
try {
Expand Down Expand Up @@ -100,11 +80,6 @@ const compile = async ({
printRollupError(e)
process.exit(1)
}

await fs.remove(paths.shellApp)
await fs.ensureDir(paths.shellApp)

await copyOutput()
} else {
return new Promise((resolve, reject) => {
reporter.debug('watching...')
Expand All @@ -117,7 +92,6 @@ const compile = async ({
if (event.code === 'START') {
reporter.print(chalk.dim('Compiling...'))
} else if (event.code === 'END') {
await copyOutput()
reporter.print(chalk.dim(' Compiled successfully!'))
resolve() // This lets us wait for the first successful compilation
} else if (event.code === 'ERROR') {
Expand All @@ -141,4 +115,4 @@ const compile = async ({
}
}

module.exports = compile
module.exports = compileLibrary
3 changes: 3 additions & 0 deletions cli/src/lib/compiler/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = {
compile: require('./compile'),
}
Loading

0 comments on commit cae7a07

Please sign in to comment.