Generate tsconfig once instead of on every webpack invocation

Avoids any potential race condition with parallel webpack invocations
This commit is contained in:
Pete Miller
2021-09-29 16:29:29 -07:00
parent ec6f175ad9
commit 4eeed9e39e
7 changed files with 145 additions and 70 deletions
+4 -1
View File
@@ -28,6 +28,8 @@ brave_common_web_compile_inputs = [
# webpack config changes warrant a re-build of all webpack builds
rebase_path("../webpack/webpack.config.js"),
rebase_path("../webpack/gen-webpack-grd.js"),
rebase_path("../webpack/path-map.js"),
rebase_path("../webpack/webpack-plugin-depfile.js"),
# typescript config changes warrant a re-build of all typescript builds
@@ -83,6 +85,7 @@ template("transpile_web_ui") {
# make sure rebuilds when common files change
inputs = brave_common_web_compile_inputs
inputs += [ "$root_gen_dir/tsconfig-webpack.json" ]
if (defined(invoker.inputs)) {
inputs += invoker.inputs
}
@@ -90,7 +93,7 @@ template("transpile_web_ui") {
deps = [
# Ensure chrome://resources/js file and typescript definitions are
# generated
"//ui/webui/resources:library",
"//brave/components/webpack:generate_tsconfig",
]
if (defined(invoker.deps)) {
deps += invoker.deps
+14
View File
@@ -0,0 +1,14 @@
action("generate_tsconfig") {
script = "//brave/script/generate-tsconfig.py"
deps = [
# Ensure chrome://resources/js file and typescript definitions are
# generated
"//ui/webui/resources:library",
]
inputs = [
"gen-tsconfig.js",
"path-map.js",
]
outputs = [ "$root_gen_dir/tsconfig-webpack.json" ]
args = [ "--root_gen_dir=" + rebase_path(root_gen_dir) ]
}
+56
View File
@@ -0,0 +1,56 @@
const fs = require('fs-extra')
const path = require('path')
const pathMap = require('./path-map')
const srcPath = path.resolve(__dirname, '../../../')
const braveSrcPath = path.join(srcPath, 'brave')
/**
* Generates a tsconfig.json file in the gen/ directory
* so that typescript can import files from cthe current build's
* gen/ directory (e.g. mojom-generated JS).
*
* @param {*} [atPath=process.env.ROOT_GEN_DIR]
* @returns void
*/
async function createGenTsConfig (atPath = process.env.ROOT_GEN_DIR) {
const configExtendsFrom = path.relative(
atPath,
path.join(braveSrcPath, 'tsconfig-webpack.json')
)
const tsConfigPath = path.join(atPath, 'tsconfig-webpack.json')
// Even though ts-loader will get the paths from webpack for module resolution
// that does not help some issues where chromium both generates ts definitions
// and has JSDoc comments for the .m.js file. Sometimes the JSDoc is incorrect
// whilst the associated .d.ts file has the correct definition. Without specifying
// the path mapping in the tsconfig.json, Typescript (via ts-loader) will use
// the JSDoc, and fail with an error. The example that prompted this is cr.sendWithPromise
// where Typescript will not see that the second parameter is an optional spread param
// and will fail with an error. Whilst this should be fixed in the chromium source,
// it's better to be explicit here so that developers get the same experience at
// both compile and design time.
const paths = {}
for (const path in pathMap) {
paths[`${path}/*`] = [`${pathMap[path]}/*`]
}
const config = {
extends: configExtendsFrom,
compilerOptions: {
paths
},
references: [
{
// This ts project is generated by //ui/webui/resources:library
path: path.join(atPath, 'ui/webui/resources/tsconfig.json')
}
]
}
await fs.writeFile(tsConfigPath, JSON.stringify(config))
return tsConfigPath
}
createGenTsConfig()
.catch(err => {
console.error(err)
process.exit(1)
})
+22
View File
@@ -0,0 +1,22 @@
// Copyright (c) 2021 The Brave Authors. All rights reserved.
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// you can obtain one at http://mozilla.org/MPL/2.0/.
const path = require('path')
module.exports = {
// Find files in the current build configurations /gen directory
'gen': process.env.ROOT_GEN_DIR,
// Generated resources at this path are available at chrome://resources and
// whilst webpack will still bundle, we keep the alias to the served path
// to minimize knowledge of specific gen/ paths and easily allow us to not bundle
// them in the future for certain build configurationa, just like chromium.
'chrome://resources': path.join(process.env.ROOT_GEN_DIR, 'ui/webui/resources/preprocessed'),
// We import brave-ui direct from source and not from package repo, so we need
// direct path to the src/ directory.
'brave-ui': path.resolve(__dirname, '../../node_modules/brave-ui/src'),
// Force same styled-components module for brave-core and brave-ui
// which ensure both repos code use the same singletons, e.g. ThemeContext.
'styled-components': path.resolve(__dirname, '../../node_modules/styled-components'),
}
+5 -69
View File
@@ -4,77 +4,14 @@
// you can obtain one at http://mozilla.org/MPL/2.0/.
const path = require('path')
const GenerateDepfilePlugin = require('./webpack-plugin-depfile')
const fs = require('fs-extra')
const webpack = require('webpack')
const GenerateDepfilePlugin = require('./webpack-plugin-depfile')
const pathMap = require('./path-map')
const srcPath = path.resolve(__dirname, '../../../')
const braveSrcPath = path.join(srcPath, 'brave')
const pathMap = {
// Find files in the current build configurations /gen directory
'gen': process.env.ROOT_GEN_DIR,
// Generated resources at this path are available at chrome://resources and
// whilst webpack will still bundle, we keep the alias to the served path
// to minimize knowledge of specific gen/ paths and easily allow us to not bundle
// them in the future for certain build configurationa, just like chromium.
'chrome://resources': path.join(process.env.ROOT_GEN_DIR, 'ui/webui/resources/preprocessed'),
// We import brave-ui direct from source and not from package repo, so we need
// direct path to the src/ directory.
'brave-ui': path.resolve(__dirname, '../../node_modules/brave-ui/src'),
// Force same styled-components module for brave-core and brave-ui
// which ensure both repos code use the same singletons, e.g. ThemeContext.
'styled-components': path.resolve(__dirname, '../../node_modules/styled-components'),
}
/**
* Generates a tsconfig.json file in the gen/ directoryc
* so that typescript can import files from cthe current build's
* gen/ directory (e.g. mojom-generated JS).
*
* @param {*} [atPath=process.env.ROOT_GEN_DIR]
* @returns void
*/
async function createGenTsConfig (atPath = process.env.ROOT_GEN_DIR) {
const configExtendsFrom = path.relative(
atPath,
path.join(braveSrcPath, 'tsconfig-webpack.json')
)
const tsConfigPath = path.join(atPath, 'tsconfig.json')
// Even though ts-loader will get the paths from webpack for module resolution
// that does not help some issues where chromium both generates ts definitions
// and has JSDoc comments for the .m.js file. Sometimes the JSDoc is incorrect
// whilst the associated .d.ts file has the correct definition. Without specifying
// the path mapping in the tsconfig.json, Typescript (via ts-loader) will use
// the JSDoc, and fail with an error. The example that prompted this is cr.sendWithPromise
// where Typescript will not see that the second parameter is an optional spread param
// and will fail with an error. Whilst this should be fixed in the chromium source,
// it's better to be explicit here so that developers get the same experience at
// both compile and design time.
const paths = {}
for (const path in pathMap) {
paths[`${path}/*`] = [`${pathMap[path]}/*`]
}
const config = {
extends: configExtendsFrom,
compilerOptions: {
paths
},
references: [
{
// This ts project is generated by //ui/webui/resources:library
path: path.join(atPath, 'ui/webui/resources/tsconfig.json')
}
]
}
await fs.writeFile(tsConfigPath, JSON.stringify(config))
return tsConfigPath
}
const tsConfigPath = path.join(process.env.ROOT_GEN_DIR, 'tsconfig-webpack.json')
module.exports = async function (env, argv) {
// TODO(petemill): only do this once per build, in a separate target
// which the webpack targets depend on.
const tsConfigPath = await createGenTsConfig()
// Webpack config object
return {
devtool: argv.mode === 'development' ? '#inline-source-map' : false,
@@ -109,9 +46,8 @@ module.exports = async function (env, argv) {
options: {
getCustomTransformers: path.join(__dirname, './webpack-ts-transformers.js'),
allowTsInNodeModules: true,
// TODO(petemill): generate in gen/ directory with baseUrl back to src/brave
// - that would remove any problems with TS analyzing types in an incorrect
// output directory (e.g. Static/ instead of Component/)
// Use generated tsconfig so that we can point at gen/ output in the
// correct build configuration output directory.
configFile: tsConfigPath
}
},
+1
View File
@@ -32,6 +32,7 @@
"pep8": "pycodestyle --max-line-length 120 -r script",
"pylint": "node ./build/commands/scripts/commands.js pylint",
"web-ui-gen-grd": "node components/webpack/gen-webpack-grd",
"web-ui-gen-tsconfig": "node components/webpack/gen-tsconfig",
"web-ui": "webpack --config components/webpack/webpack.config.js --colors",
"build-storybook": "build-storybook -c .storybook -o .storybook-out",
"storybook": "start-storybook",
+43
View File
@@ -0,0 +1,43 @@
import argparse
import os
import sys
from lib.util import execute_stdout, scoped_cwd
NPM = 'npm'
if sys.platform in ['win32', 'cygwin']:
NPM += '.cmd'
def main():
args = parse_args()
root_gen_dir = args.root_gen_dir[0]
generate_tsconfig(root_gen_dir)
def parse_args():
parser = argparse.ArgumentParser(description='Generate tsconfig')
parser.add_argument('--root_gen_dir', nargs=1)
args = parser.parse_args()
# validate args
if (args.root_gen_dir is None or
len(args.root_gen_dir) != 1 or
len(args.root_gen_dir[0]) == 0):
raise Exception("root_gen_dir argument was not specified correctly")
# args are valid
return args
def generate_tsconfig(root_gen_dir, env=None):
if env is None:
env = os.environ.copy()
args = [NPM, 'run', 'web-ui-gen-tsconfig']
env["ROOT_GEN_DIR"] = root_gen_dir
dirname = os.path.abspath(os.path.join(__file__, '..', '..'))
with scoped_cwd(dirname):
execute_stdout(args, env)
if __name__ == '__main__':
sys.exit(main())