-
Notifications
You must be signed in to change notification settings - Fork 4.2k
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
Build: Add building/watching support to Gutenberg packages #6708
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
build | ||
build-module | ||
coverage | ||
node_modules | ||
vendor |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
# Directories/files that may be generated by this project | ||
build | ||
build-module | ||
coverage | ||
/hooks | ||
node_modules | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
/** | ||
* script to build WordPress packages into `build/` directory. | ||
* | ||
* Example: | ||
* node ./scripts/build.js | ||
*/ | ||
|
||
/** | ||
* External Dependenceis | ||
*/ | ||
const fs = require( 'fs' ); | ||
const path = require( 'path' ); | ||
const glob = require( 'glob' ); | ||
const babel = require( 'babel-core' ); | ||
const chalk = require( 'chalk' ); | ||
const mkdirp = require( 'mkdirp' ); | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
const getPackages = require( './get-packages' ); | ||
|
||
/** | ||
* Module Constants | ||
*/ | ||
const PACKAGES_DIR = path.resolve( __dirname, '../../packages' ); | ||
const SRC_DIR = 'src'; | ||
const BUILD_DIR = { | ||
main: 'build', | ||
module: 'build-module', | ||
}; | ||
const DONE = chalk.reset.inverse.bold.green( ' DONE ' ); | ||
|
||
/** | ||
* Babel Configuration | ||
*/ | ||
const babelDefaultConfig = require( '@wordpress/babel-preset-default' ); | ||
babelDefaultConfig.babelrc = false; | ||
const presetEnvConfig = babelDefaultConfig.presets[ 0 ][ 1 ]; | ||
const babelConfigs = { | ||
main: Object.assign( | ||
{}, | ||
babelDefaultConfig, | ||
{ presets: [ | ||
[ 'env', Object.assign( | ||
{}, | ||
presetEnvConfig, | ||
{ modules: 'commonjs' }, | ||
) ], | ||
] } | ||
), | ||
module: babelDefaultConfig, | ||
}; | ||
|
||
/** | ||
* Get the package name for a specified file | ||
* | ||
* @param {string} file File name | ||
* @return {string} Package name | ||
*/ | ||
function getPackageName( file ) { | ||
return path.relative( PACKAGES_DIR, file ).split( path.sep )[ 0 ]; | ||
} | ||
|
||
/** | ||
* Get Build Path for a specified file | ||
* | ||
* @param {string} file File to build | ||
* @param {string} buildFolder Output folder | ||
* @return {string} Build path | ||
*/ | ||
function getBuildPath( file, buildFolder ) { | ||
const pkgName = getPackageName( file ); | ||
const pkgSrcPath = path.resolve( PACKAGES_DIR, pkgName, SRC_DIR ); | ||
const pkgBuildPath = path.resolve( PACKAGES_DIR, pkgName, buildFolder ); | ||
const relativeToSrcPath = path.relative( pkgSrcPath, file ); | ||
return path.resolve( pkgBuildPath, relativeToSrcPath ); | ||
} | ||
|
||
/** | ||
* Build a file for the required environments (node and ES5) | ||
* | ||
* @param {string} file File path to build | ||
* @param {boolean} silent Show logs | ||
*/ | ||
function buildFile( file, silent ) { | ||
buildFileFor( file, silent, 'main' ); | ||
buildFileFor( file, silent, 'module' ); | ||
} | ||
|
||
/** | ||
* Build a file for a specific environment | ||
* | ||
* @param {string} file File path to build | ||
* @param {boolean} silent Show logs | ||
* @param {string} environment Dist environment (node or es5) | ||
*/ | ||
function buildFileFor( file, silent, environment ) { | ||
const buildDir = BUILD_DIR[ environment ]; | ||
const destPath = getBuildPath( file, buildDir ); | ||
const babelOptions = babelConfigs[ environment ]; | ||
|
||
mkdirp.sync( path.dirname( destPath ) ); | ||
const transformed = babel.transformFileSync( file, babelOptions ).code; | ||
fs.writeFileSync( destPath, transformed ); | ||
if ( ! silent ) { | ||
process.stdout.write( | ||
chalk.green( ' \u2022 ' ) + | ||
path.relative( PACKAGES_DIR, file ) + | ||
chalk.green( ' \u21D2 ' ) + | ||
path.relative( PACKAGES_DIR, destPath ) + | ||
'\n' | ||
); | ||
} | ||
} | ||
|
||
/** | ||
* Build the provided package path | ||
* | ||
* @param {string} packagePath absolute package path | ||
*/ | ||
function buildPackage( packagePath ) { | ||
const srcDir = path.resolve( packagePath, SRC_DIR ); | ||
const files = glob.sync( srcDir + '/**/*.js', { nodir: true } ) | ||
.filter( ( file ) => ! /\.test\.js/.test( file ) ); | ||
|
||
process.stdout.write( `${ path.basename( packagePath ) }\n` ); | ||
|
||
files.forEach( ( file ) => buildFile( file, true ) ); | ||
process.stdout.write( `${ DONE }\n` ); | ||
} | ||
|
||
const files = process.argv.slice( 2 ); | ||
|
||
if ( files.length ) { | ||
files.forEach( buildFile ); | ||
} else { | ||
process.stdout.write( chalk.inverse( '>> Building packages \n' ) ); | ||
getPackages() | ||
.forEach( buildPackage ); | ||
process.stdout.write( '\n' ); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
/** | ||
* External Dependenceis | ||
*/ | ||
const fs = require( 'fs' ); | ||
const path = require( 'path' ); | ||
|
||
/** | ||
* Module Constants | ||
*/ | ||
const PACKAGES_DIR = path.resolve( __dirname, '../../packages' ); | ||
|
||
/** | ||
* Returns the absolute path of all WordPress packages | ||
* | ||
* @return {Array} Package paths | ||
*/ | ||
function getPackages() { | ||
return fs | ||
.readdirSync( PACKAGES_DIR ) | ||
.map( ( file ) => path.resolve( PACKAGES_DIR, file ) ) | ||
.filter( ( f ) => fs.lstatSync( path.resolve( f ) ).isDirectory() ); | ||
} | ||
|
||
module.exports = getPackages; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
/** | ||
* External dependencies | ||
*/ | ||
const fs = require( 'fs' ); | ||
const { execSync } = require( 'child_process' ); | ||
const path = require( 'path' ); | ||
const chalk = require( 'chalk' ); | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
const getPackages = require( './get-packages' ); | ||
|
||
const BUILD_CMD = `node ${ path.resolve( __dirname, './build.js' ) }`; | ||
|
||
let filesToBuild = new Map(); | ||
|
||
const exists = ( filename ) => { | ||
try { | ||
return fs.statSync( filename ).isFile(); | ||
} catch ( e ) {} | ||
return false; | ||
}; | ||
const rebuild = ( filename ) => filesToBuild.set( filename, true ); | ||
|
||
getPackages().forEach( ( p ) => { | ||
const srcDir = path.resolve( p, 'src' ); | ||
try { | ||
fs.accessSync( srcDir, fs.F_OK ); | ||
fs.watch( path.resolve( p, 'src' ), { recursive: true }, ( event, filename ) => { | ||
const filePath = path.resolve( srcDir, filename ); | ||
|
||
if ( ( event === 'change' || event === 'rename' ) && exists( filePath ) ) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. According to https://nodejs.org/docs/latest/api/fs.html#fs_fs_watch_filename_options_listener |
||
// eslint-disable-next-line no-console | ||
console.log( chalk.green( '->' ), `${ event }: ${ filename }` ); | ||
rebuild( filePath ); | ||
} else { | ||
const buildFile = path.resolve( srcDir, '..', 'build', filename ); | ||
try { | ||
fs.unlinkSync( buildFile ); | ||
process.stdout.write( | ||
chalk.red( ' \u2022 ' ) + | ||
path.relative( path.resolve( srcDir, '..', '..' ), buildFile ) + | ||
' (deleted)' + | ||
'\n' | ||
); | ||
} catch ( e ) {} | ||
} | ||
} ); | ||
} catch ( e ) { | ||
// doesn't exist | ||
} | ||
} ); | ||
|
||
setInterval( () => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Might be worth considering replacing this with a debounced function that's triggered in |
||
const files = Array.from( filesToBuild.keys() ); | ||
if ( files.length ) { | ||
filesToBuild = new Map(); | ||
try { | ||
execSync( `${ BUILD_CMD } ${ files.join( ' ' ) }`, { stdio: [ 0, 1, 2 ] } ); | ||
} catch ( e ) {} | ||
} | ||
}, 100 ); | ||
|
||
// eslint-disable-next-line no-console | ||
console.log( chalk.red( '->' ), chalk.cyan( 'Watching for changes...' ) ); |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -123,12 +123,14 @@ | |
}, | ||
"scripts": { | ||
"prebuild": "check-node-version --package", | ||
"build": "cross-env NODE_ENV=production webpack", | ||
"build:packages": "rimraf ./packages/*/build ./packages/*/build-module && node ./bin/packages/build.js", | ||
"build": "npm run build:packages && cross-env NODE_ENV=production webpack", | ||
"lint": "eslint .", | ||
"lint:fix": "eslint . --fix", | ||
"lint-php": "docker-compose run --rm composer run-script lint", | ||
"predev": "check-node-version --package", | ||
"dev": "cross-env webpack --watch", | ||
"dev": "npm run build:packages && concurrently \"cross-env webpack --watch\" \"npm run dev:packages\"", | ||
"dev:packages": "node ./bin/packages/watch.js", | ||
"test": "npm run lint && npm run test-unit", | ||
"test-php": "npm run lint-php && npm run test-unit-php", | ||
"ci": "concurrently \"npm run lint && npm run build\" \"npm run test-unit:coverage-ci\"", | ||
|
@@ -137,7 +139,7 @@ | |
"fixtures:generate": "npm run fixtures:server-registered && cross-env GENERATE_MISSING_FIXTURES=y npm run test-unit", | ||
"fixtures:regenerate": "npm run fixtures:clean && npm run fixtures:generate", | ||
"package-plugin": "./bin/build-plugin-zip.sh", | ||
"postinstall": "lerna bootstrap --hoist", | ||
"postinstall": "lerna bootstrap --hoist && npm run build:packages", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We might need it later when we start referencing internal packages. This was the case for |
||
"pot-to-php": "./bin/pot-to-php.js", | ||
"publish:check": "lerna updated", | ||
"publish:dev": "lerna publish --npm-tag next", | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should load setup from
package.json
- we use some overrides.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should use a separate config here. The root config could contain things we don't want here.
For example: generating the "messages.pot" file should not happen here but should happen when webpack performs the building (it's not something we want to include per module).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, this is also what I figured out when trying to update this config yesterday.
I published new version of Babel preset to include missing async generator functions plugin.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm wondering something though about the "messages.pot" generation :). babel may change variable names and function names and this could cause the make-pot to fail and not find the right strings. So in the end it might be better to include it here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#6893 will cover the support for async generator functions.