chore(typescript): compile with babel, lint everything with tsc (#6872)

This commit is contained in:
Joel Einbinder 2021-06-23 18:01:48 -07:00 committed by GitHub
parent b3cc683e6b
commit 1a6d46d844
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 1352 additions and 1353 deletions

18
.babelrc Normal file
View File

@ -0,0 +1,18 @@
{
"assumptions": {
"setPublicClassFields": true
},
"presets": [
"@babel/preset-typescript"
],
"plugins": [
"@babel/plugin-proposal-export-namespace-from",
"@babel/plugin-proposal-class-properties",
"@babel/plugin-proposal-optional-chaining",
"@babel/plugin-transform-modules-commonjs"
],
"ignore": [
"**/*.d.ts",
"src/injected/**/*"
]
}

View File

@ -2,9 +2,6 @@ test/assets/modernizr.js
lib/
*.js
src/generated/*
src/server/chromium/protocol.ts
src/server/firefox/protocol.ts
src/server/webkit/protocol.ts
src/third_party/
/types/*
/index.d.ts

View File

@ -32,7 +32,7 @@ try {
console.log(`Rebuilding installer...`);
try {
execSync('npm run tsc-installer', {
execSync('npm run build-installer', {
stdio: ['inherit', 'inherit', 'inherit'],
});
} catch (e) {

2228
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -19,13 +19,13 @@
"test": "npm run basetest -- --config=tests/config/default.config.ts",
"eslint": "[ \"$CI\" = true ] && eslint --quiet -f codeframe --ext ts . || eslint --ext ts .",
"tsc": "tsc -p .",
"tsc-installer": "tsc -p ./src/install/tsconfig.json",
"build-installer": "babel -s --extensions \".ts\" --out-dir lib/install/ src/install && babel -s --extensions \".ts\" --out-dir lib/utils/ src/utils",
"doc": "node utils/doclint/cli.js",
"lint": "npm run eslint && npm run tsc && npm run doc && npm run check-deps && node utils/generate_channels.js && node utils/generate_types/ --check-clean && npm run test-types",
"clean": "rimraf lib",
"clean": "rimraf lib && rimraf src/generated/",
"prepare": "node install-from-github.js",
"build": "node utils/build/build.js",
"watch": "node utils/build/build.js --watch",
"watch": "node utils/build/build.js --watch --lint",
"test-types": "node utils/generate_types/ && npx -p typescript@3.7.5 tsc -p utils/generate_types/test/tsconfig.json && tsc -p ./tests/",
"roll": "node utils/roll_browser.js",
"check-deps": "node utils/check_deps.js",
@ -40,6 +40,7 @@
},
"DEPS-NOTE": "Any non-test dependency must be added to the build_package.js script as well",
"dependencies": {
"@babel/cli": "^7.14.3",
"@babel/code-frame": "^7.12.13",
"@babel/core": "^7.14.0",
"@babel/plugin-proposal-class-properties": "^7.13.0",
@ -57,6 +58,7 @@
"@babel/plugin-syntax-optional-catch-binding": "^7.8.3",
"@babel/plugin-transform-modules-commonjs": "^7.14.0",
"@babel/preset-typescript": "^7.13.0",
"babel-loader": "^8.2.2",
"colors": "^1.4.0",
"commander": "^6.1.0",
"debug": "^4.1.1",
@ -80,6 +82,7 @@
"yazl": "^2.5.1"
},
"devDependencies": {
"@babel/preset-react": "^7.14.5",
"@types/babel__code-frame": "^7.0.2",
"@types/babel__core": "^7.1.14",
"@types/debug": "^4.1.5",
@ -120,7 +123,6 @@
"react-dom": "^17.0.1",
"socksv5": "0.0.6",
"style-loader": "^1.2.1",
"ts-loader": "^8.0.3",
"typescript": "=4.2.4",
"webpack": "^4.44.2",
"webpack-cli": "^3.3.12",

View File

@ -18,7 +18,7 @@ export { Accessibility } from './accessibility';
export { Android, AndroidDevice, AndroidWebView, AndroidInput, AndroidSocket } from './android';
export { Browser } from './browser';
export { BrowserContext } from './browserContext';
export { BrowserServer } from './browserType';
export type { BrowserServer } from './browserType';
export { BrowserType } from './browserType';
export { ConsoleMessage } from './consoleMessage';
export { Coverage } from './coverage';
@ -27,7 +27,7 @@ export { Download } from './download';
export { Electron, ElectronApplication } from './electron';
export { ElementHandle } from './elementHandle';
export { FileChooser } from './fileChooser';
export { Logger } from './types';
export type { Logger } from './types';
export { TimeoutError } from '../utils/errors';
export { Frame } from './frame';
export { Keyboard, Mouse, Touchscreen } from './input';

View File

@ -1,6 +0,0 @@
{
// This config exists so that we can quickly compile just an installer.
"extends": "../../tsconfig.json",
"include": ["./*.ts"],
"exclude": ["node_modules"]
}

View File

@ -1,49 +0,0 @@
/**
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const path = require('path');
const InlineSource = require('./webpack-inline-source-plugin.js');
/** @type {import('webpack').Configuration} */
module.exports = {
entry: path.join(__dirname, 'injectedScript.ts'),
mode: process.env.NODE_ENV === 'production' ? 'production' : 'development',
devtool: false,
module: {
rules: [
{
test: /\.(j|t)sx?$/,
loader: 'ts-loader',
options: {
transpileOnly: true
},
exclude: /node_modules/
}
]
},
resolve: {
extensions: [ '.tsx', '.ts', '.js' ]
},
output: {
filename: 'injectedScriptSource.js',
libraryTarget: 'var',
libraryExport: 'default',
library: 'pwExport',
path: path.resolve(__dirname, '../../../lib/server/injected/packed')
},
plugins: [
new InlineSource(path.join(__dirname, '..', '..', 'generated', 'injectedScriptSource.ts')),
]
};

View File

@ -1,50 +0,0 @@
/**
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const path = require('path');
const InlineSource = require('./webpack-inline-source-plugin.js');
/** @type {import('webpack').Configuration} */
module.exports = {
entry: path.join(__dirname, 'utilityScript.ts'),
mode: process.env.NODE_ENV === 'production' ? 'production' : 'development',
devtool: false,
module: {
rules: [
{
test: /\.(j|t)sx?$/,
loader: 'ts-loader',
options: {
transpileOnly: true
},
exclude: /node_modules/
}
]
},
resolve: {
extensions: [ '.tsx', '.ts', '.js' ]
},
output: {
libraryTarget: 'var',
library: 'pwExport',
libraryExport: 'default',
filename: 'utilityScriptSource.js',
path: path.resolve(__dirname, '../../../lib/server/injected/packed')
},
plugins: [
new InlineSource(path.join(__dirname, '..', '..', 'generated', 'utilityScriptSource.ts')),
]
};

View File

@ -1,37 +0,0 @@
/**
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const fs = require('fs');
const path = require('path');
module.exports = class InlineSource {
constructor(outFile) {
this.outFile = outFile;
}
/**
* @param {import('webpack').Compiler} compiler
*/
apply(compiler) {
compiler.hooks.emit.tapAsync('InlineSource', (compilation, callback) => {
const source = compilation.assets[path.basename(this.outFile).replace('.ts', '.js')].source();
fs.mkdirSync(path.dirname(this.outFile), { recursive: true });
const newSource = 'export const source = ' + JSON.stringify(source) + ';';
fs.writeFileSync(this.outFile, newSource);
callback();
});
}
};

View File

@ -0,0 +1,80 @@
/**
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const path = require('path');
const fs = require('fs');
class InlineSource {
/**
* @param {string[]} outFiles
*/
constructor(outFiles) {
this.outFiles = outFiles;
}
/**
* @param {import('webpack').Compiler} compiler
*/
apply(compiler) {
compiler.hooks.emit.tapAsync('InlineSource', (compilation, callback) => {
for (const outFile of this.outFiles) {
const source = compilation.assets[path.basename(outFile).replace('.ts', '.js')].source();
fs.mkdirSync(path.dirname(outFile), { recursive: true });
const newSource = 'export const source = ' + JSON.stringify(source) + ';';
fs.writeFileSync(outFile, newSource);
}
callback();
});
}
}
const entry = {
utilityScriptSource: path.join(__dirname, 'utilityScript.ts'),
injectedScriptSource: path.join(__dirname, 'injectedScript.ts'),
consoleApiSource: path.join(__dirname, '..', 'supplements', 'injected', 'consoleApi.ts'),
recorderSource: path.join(__dirname, '..', 'supplements', 'injected', 'recorder.ts'),
}
/** @type {import('webpack').Configuration} */
module.exports = {
entry,
mode: process.env.NODE_ENV === 'production' ? 'production' : 'development',
devtool: false,
module: {
rules: [
{
test: /\.(j|t)sx?$/,
loader: 'babel-loader',
exclude: /node_modules/
}
]
},
resolve: {
extensions: [ '.tsx', '.ts', '.js' ]
},
output: {
libraryTarget: 'var',
library: 'pwExport',
libraryExport: 'default',
filename: '[name].js',
path: path.resolve(__dirname, '../../../lib/server/injected/packed')
},
plugins: [
new InlineSource(
Object.keys(entry).map(x => path.join(__dirname, '..', '..', 'generated', x + '.ts'))
),
]
};

View File

@ -1,50 +0,0 @@
/**
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const path = require('path');
const InlineSource = require('../../injected/webpack-inline-source-plugin');
/** @type {import('webpack').Configuration} */
module.exports = {
entry: path.join(__dirname, 'consoleApi.ts'),
mode: process.env.NODE_ENV === 'production' ? 'production' : 'development',
devtool: false,
module: {
rules: [
{
test: /\.(j|t)sx?$/,
loader: 'ts-loader',
options: {
transpileOnly: true
},
exclude: /node_modules/
}
]
},
resolve: {
extensions: [ '.tsx', '.ts', '.js' ]
},
output: {
libraryTarget: 'var',
library: 'pwExport',
libraryExport: 'default',
filename: 'consoleApiSource.js',
path: path.resolve(__dirname, '../../../../lib/server/injected/packed')
},
plugins: [
new InlineSource(path.join(__dirname, '..', '..', '..', 'generated', 'consoleApiSource.ts')),
]
};

View File

@ -1,50 +0,0 @@
/**
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const path = require('path');
const InlineSource = require('../../injected/webpack-inline-source-plugin');
/** @type {import('webpack').Configuration} */
module.exports = {
entry: path.join(__dirname, 'recorder.ts'),
mode: process.env.NODE_ENV === 'production' ? 'production' : 'development',
devtool: false,
module: {
rules: [
{
test: /\.(j|t)sx?$/,
loader: 'ts-loader',
options: {
transpileOnly: true
},
exclude: /node_modules/
}
]
},
resolve: {
extensions: [ '.tsx', '.ts', '.js' ]
},
output: {
libraryTarget: 'var',
library: 'pwExport',
libraryExport: 'default',
filename: 'recorderSource.js',
path: path.resolve(__dirname, '../../../../lib/server/injected/packed')
},
plugins: [
new InlineSource(path.join(__dirname, '..', '..', '..', 'generated', 'recorderSource.ts')),
]
};

View File

@ -199,7 +199,6 @@ export class WKBrowser extends Browser {
export class WKBrowserContext extends BrowserContext {
readonly _browser: WKBrowser;
readonly _browserContextId: string | undefined;
readonly _evaluateOnNewDocumentSources: string[];
constructor(browser: WKBrowser, browserContextId: string | undefined, options: types.BrowserContextOptions) {

View File

@ -19,7 +19,13 @@ module.exports = {
rules: [
{
test: /\.(j|t)sx?$/,
use: 'ts-loader',
loader: 'babel-loader',
options: {
presets: [
"@babel/preset-typescript",
"@babel/preset-react"
]
},
exclude: /node_modules/
},
{

View File

@ -19,7 +19,13 @@ module.exports = {
rules: [
{
test: /\.(j|t)sx?$/,
use: 'ts-loader',
loader: 'babel-loader',
options: {
presets: [
"@babel/preset-typescript",
"@babel/preset-react"
]
},
exclude: /node_modules/
},
{

View File

@ -3,18 +3,14 @@
"target": "ES2019",
"module": "commonjs",
"lib": ["esnext", "dom", "DOM.Iterable"],
"sourceMap": true,
"esModuleInterop": true,
"rootDir": "./src",
"outDir": "./lib",
"strict": true,
"allowJs": true,
"declaration": false,
"jsx": "react",
"downlevelIteration": true,
"resolveJsonModule": true,
"noEmit": true,
},
"compileOnSave": true,
"include": ["src/**/*.ts", "src/**/*.js", "src/server/deviceDescriptorsSource.json"],
"exclude": ["node_modules", "src/.eslintrc.js", "src/web/**", "src/**/*webpack.config.js"]
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.js", "src/server/deviceDescriptorsSource.json"],
"exclude": ["node_modules", "src/.eslintrc.js", "src/**/*webpack.config.js"]
}

View File

@ -17,11 +17,14 @@
const child_process = require('child_process');
const path = require('path');
const chokidar = require('chokidar');
const fs = require('fs');
const steps = [];
const onChanges = [];
const copyFiles = [];
const watchMode = process.argv.slice(2).includes('--watch');
const lintMode = process.argv.slice(2).includes('--lint');
const ROOT = path.join(__dirname, '..', '..');
function filePath(relative) {
@ -47,9 +50,15 @@ function runWatch() {
process.on('exit', () => spawns.forEach(s => s.kill()));
for (const onChange of onChanges)
runOnChanges(onChange.inputs, onChange.script);
for (const {files, from, to, ignored} of copyFiles) {
const watcher = chokidar.watch([filePath(files)], {ignored});
watcher.on('all', (event, file) => {
copyFile(file, from, to);
});
}
}
function runBuild() {
async function runBuild() {
function runStep(command, args, shell) {
const out = child_process.spawnSync(command, args, { stdio: 'inherit', shell });
if (out.status)
@ -62,14 +71,27 @@ function runBuild() {
if (!onChange.committed)
runStep('node', [filePath(onChange.script)], false);
}
for (const {files, from, to, ignored} of copyFiles) {
const watcher = chokidar.watch([filePath(files)], {
ignored
});
watcher.on('add', file => {
copyFile(file, from, to);
});
await new Promise(x => watcher.once('ready', x));
watcher.close();
}
}
function copyFile(file, from, to) {
const destination = path.resolve(filePath(to), path.relative(filePath(from), file));
fs.mkdirSync(path.dirname(destination), { recursive: true });
fs.copyFileSync(file, destination);
}
// Build injected scripts.
const webPackFiles = [
'src/server/injected/injectedScript.webpack.config.js',
'src/server/injected/utilityScript.webpack.config.js',
'src/server/supplements/injected/consoleApi.webpack.config.js',
'src/server/supplements/injected/recorder.webpack.config.js',
'src/server/injected/webpack.config.js',
'src/web/traceViewer/webpack.config.js',
'src/web/recorder/webpack.config.js',
];
@ -84,10 +106,10 @@ for (const file of webPackFiles) {
});
}
// Run typescript.
// Run Babel.
steps.push({
command: 'npx',
args: ['tsc', ...(watchMode ? ['-w', '--preserveWatchOutput'] : []), '-p', filePath('.')],
args: ['babel', ...(watchMode ? ['-w'] : []), '-s', '--extensions', '.ts', '--out-dir', filePath('./lib/'), filePath('./src/')],
shell: true,
});
@ -107,17 +129,44 @@ onChanges.push({
'docs/src/api/',
'utils/generate_types/overrides.d.ts',
'utils/generate_types/exported.json',
'src/server/chromium/protocol.ts',
'src/server/chromium/protocol.d.ts',
'src/trace/traceTypes.ts',
],
script: 'utils/generate_types/index.js',
});
// Copy images.
steps.push({
command: process.platform === 'win32' ? 'copy' : 'cp',
args: [filePath('src/web/recorder/*.png'), filePath('lib/web/recorder/')],
shell: true,
// The recorder has an app_icon.png that needs to be copied.
copyFiles.push({
files: 'src/web/recorder/*.png',
from: 'src',
to: 'lib',
});
// Babel doesn't touch JS files, so copy them manually.
// For example: diff_match_patch.js
copyFiles.push({
files: 'src/**/*.js',
from: 'src',
to: 'lib',
ignored: ['**/.eslintrc.js', '**/*webpack.config.js', '**/injected/**/*']
});
// Sometimes we require JSON files that babel ignores.
// For example, deviceDescriptorsSource.json
copyFiles.push({
files: 'src/**/*.json',
ignored: ['**/injected/**/*'],
from: 'src',
to: 'lib',
});
if (lintMode) {
// Run TypeScript for type chekcing.
steps.push({
command: 'npx',
args: ['tsc', ...(watchMode ? ['-w'] : []), '-p', filePath('.')],
shell: true,
});
}
watchMode ? runWatch() : runBuild();

View File

@ -112,7 +112,7 @@ DEPS['src/protocol/'] = ['src/utils/'];
DEPS['src/install/'] = ['src/utils/'];
// Client depends on chromium protocol for types.
DEPS['src/client/'] = ['src/common/', 'src/utils/', 'src/protocol/', 'src/server/chromium/protocol.ts'];
DEPS['src/client/'] = ['src/common/', 'src/utils/', 'src/protocol/', 'src/server/chromium/protocol.d.ts'];
DEPS['src/outofprocess.ts'] = ['src/client/', 'src/protocol/'];
DEPS['src/dispatchers/'] = ['src/common/', 'src/utils/', 'src/protocol/', 'src/server/**'];

View File

@ -36,7 +36,7 @@ let hadChanges = false;
const typesDir = path.join(PROJECT_DIR, 'types');
if (!fs.existsSync(typesDir))
fs.mkdirSync(typesDir)
writeFile(path.join(typesDir, 'protocol.d.ts'), fs.readFileSync(path.join(PROJECT_DIR, 'src', 'server', 'chromium', 'protocol.ts'), 'utf8'));
writeFile(path.join(typesDir, 'protocol.d.ts'), fs.readFileSync(path.join(PROJECT_DIR, 'src', 'server', 'chromium', 'protocol.d.ts'), 'utf8'));
documentation = parseApi(path.join(PROJECT_DIR, 'docs', 'src', 'api'));
documentation.filterForLanguage('js');
documentation.copyDocsFromSuperclasses([]);

View File

@ -15,7 +15,7 @@ async function generateProtocol(name, executablePath) {
}
async function generateChromiumProtocol(executablePath) {
const outputPath = path.join(__dirname, '../../src/server/chromium/protocol.ts');
const outputPath = path.join(__dirname, '../../src/server/chromium/protocol.d.ts');
const playwright = require('../../index').chromium;
const browser = await playwright.launch({ executablePath, args: ['--remote-debugging-port=9339'] });
const page = await browser.newPage();
@ -23,14 +23,14 @@ async function generateChromiumProtocol(executablePath) {
const json = JSON.parse(await page.evaluate(() => document.documentElement.innerText));
await browser.close();
await fs.promises.writeFile(outputPath, jsonToTS(json));
console.log(`Wrote protocol.ts to ${path.relative(process.cwd(), outputPath)}`);
console.log(`Wrote protocol.d.ts to ${path.relative(process.cwd(), outputPath)}`);
}
async function generateWebKitProtocol(folderPath) {
const outputPath = path.join(__dirname, '../../src/server/webkit/protocol.ts');
const outputPath = path.join(__dirname, '../../src/server/webkit/protocol.d.ts');
const json = JSON.parse(await fs.promises.readFile(path.join(folderPath, '../protocol.json'), 'utf8'));
await fs.promises.writeFile(outputPath, jsonToTS({domains: json}));
console.log(`Wrote protocol.ts for WebKit to ${path.relative(process.cwd(), outputPath)}`);
console.log(`Wrote protocol.d.ts for WebKit to ${path.relative(process.cwd(), outputPath)}`);
}
const conditionFilter = command => command.condition !== 'defined(WTF_PLATFORM_IOS_FAMILY) && WTF_PLATFORM_IOS_FAMILY';
@ -124,7 +124,7 @@ function typeOfProperty(property, domain) {
}
async function generateFirefoxProtocol(executablePath) {
const outputPath = path.join(__dirname, '../../src/server/firefox/protocol.ts');
const outputPath = path.join(__dirname, '../../src/server/firefox/protocol.d.ts');
const omnija = os.platform() === 'darwin' ?
path.join(executablePath, '../../Resources/omni.ja') :
path.join(executablePath, '../omni.ja');
@ -168,7 +168,7 @@ async function generateFirefoxProtocol(executablePath) {
}
const json = vm.runInContext(`(${inject})();${protocolJSCode}; this.protocol;`, ctx);
await fs.promises.writeFile(outputPath, firefoxJSONToTS(json));
console.log(`Wrote protocol.ts for Firefox to ${path.relative(process.cwd(), outputPath)}`);
console.log(`Wrote protocol.d.ts for Firefox to ${path.relative(process.cwd(), outputPath)}`);
}
function firefoxJSONToTS(json) {