diff --git a/.circleci/config.yml b/.circleci/config.yml index 86d85a1b9..b281a25de 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -576,7 +576,7 @@ jobs: - persist_to_workspace: root: . paths: - - development/ts-migration-dashboard/build + - development/ts-migration-dashboard/build/final test-yarn-dedupe: executor: node-browsers @@ -1013,7 +1013,7 @@ jobs: path: storybook-build destination: storybook - store_artifacts: - path: development/ts-migration-dashboard/build + path: development/ts-migration-dashboard/build/final destination: ts-migration-dashboard - run: name: Set branch parent commit env var diff --git a/.gitignore b/.gitignore index 11d9f53ea..aa4628d7a 100644 --- a/.gitignore +++ b/.gitignore @@ -34,7 +34,6 @@ dist builds/ builds.zip development/ts-migration-dashboard/build -development/ts-migration-dashboard/intermediate test-artifacts test-builds diff --git a/development/ts-migration-dashboard/src/App.tsx b/development/ts-migration-dashboard/app/components/App.tsx similarity index 80% rename from development/ts-migration-dashboard/src/App.tsx rename to development/ts-migration-dashboard/app/components/App.tsx index d799815b4..3a196f7ca 100644 --- a/development/ts-migration-dashboard/src/App.tsx +++ b/development/ts-migration-dashboard/app/components/App.tsx @@ -1,21 +1,7 @@ import React from 'react'; import classnames from 'classnames'; import { Tooltip as ReactTippy } from 'react-tippy'; -import { ModulePartition } from '../scripts/build-module-partitions'; - -// The `brfs` transform for browserify calls `fs.readLineSync` and -// `path.resolve` at build time and inlines file contents into the source code. -// To accomplish this we have to bring in `fs` and `path` using `require` and -// not `import`. This is weird in a TypeScript file, and typescript-eslint -// (rightly) complains about this, but it's actually okay because the above -// `import` lines will actually get turned into `require`s anyway before passing -// through the rest of browserify. However, `brfs` should handle this. There is -// an active bug for this, but there isn't a known workaround yet: -// -/* eslint-disable-next-line @typescript-eslint/no-require-imports,@typescript-eslint/no-var-requires */ -const fs = require('fs'); -/* eslint-disable-next-line @typescript-eslint/no-require-imports,@typescript-eslint/no-var-requires */ -const path = require('path'); +import { readPartitionsFile } from '../../common/partitions-file'; type Summary = { numConvertedFiles: number; @@ -27,14 +13,7 @@ function calculatePercentageComplete(summary: Summary) { } export default function App() { - const partitions = JSON.parse( - fs.readFileSync( - path.resolve(__dirname, '../intermediate/partitions.json'), - { - encoding: 'utf-8', - }, - ), - ) as ModulePartition[]; + const partitions = readPartitionsFile(); const allFiles = partitions.flatMap((partition) => { return partition.children; diff --git a/development/ts-migration-dashboard/app/index.scss b/development/ts-migration-dashboard/app/index.scss new file mode 100644 index 000000000..6125516d4 --- /dev/null +++ b/development/ts-migration-dashboard/app/index.scss @@ -0,0 +1,4 @@ +@import '../../../ui/css/reset'; +@import './styles/tippy'; +@import './styles/native-elements'; +@import './styles/custom-elements'; diff --git a/development/ts-migration-dashboard/src/index.tsx b/development/ts-migration-dashboard/app/index.tsx similarity index 81% rename from development/ts-migration-dashboard/src/index.tsx rename to development/ts-migration-dashboard/app/index.tsx index 5c458b5ae..a18d9c2ab 100644 --- a/development/ts-migration-dashboard/src/index.tsx +++ b/development/ts-migration-dashboard/app/index.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import ReactDOM from 'react-dom'; -import App from './App'; +import App from './components/App'; const appElement = document.querySelector('#app'); diff --git a/development/ts-migration-dashboard/src/public/images/metamask-fox.svg b/development/ts-migration-dashboard/app/public/images/metamask-fox.svg similarity index 100% rename from development/ts-migration-dashboard/src/public/images/metamask-fox.svg rename to development/ts-migration-dashboard/app/public/images/metamask-fox.svg diff --git a/development/ts-migration-dashboard/src/public/index.html b/development/ts-migration-dashboard/app/public/index.html similarity index 100% rename from development/ts-migration-dashboard/src/public/index.html rename to development/ts-migration-dashboard/app/public/index.html diff --git a/development/ts-migration-dashboard/src/index.scss b/development/ts-migration-dashboard/app/styles/custom-elements.scss similarity index 76% rename from development/ts-migration-dashboard/src/index.scss rename to development/ts-migration-dashboard/app/styles/custom-elements.scss index 863fb52c2..985a423ab 100644 --- a/development/ts-migration-dashboard/src/index.scss +++ b/development/ts-migration-dashboard/app/styles/custom-elements.scss @@ -1,61 +1,3 @@ -@import '../../../ui/css/reset'; -@import './tippy'; - -/* Native elements */ - -* { - box-sizing: border-box; -} - -html { - font-family: - -apple-system, - BlinkMacSystemFont, - "Segoe UI", - Helvetica, - Arial, - sans-serif, - "Apple Color Emoji", - "Segoe UI Emoji"; - font-size: 16px; -} - -body { - padding: 2rem; -} - -p:not(:last-child) { - margin-bottom: 1rem; -} - -code { - font-size: 0.85em; - font-family: - ui-monospace, - SFMono-Regular, - SF Mono, - Menlo, - Consolas, - Liberation Mono, - monospace; -} - -strong { - font-weight: bold; -} - -em { - font-style: italic; -} - -ul { - list-style: disc; - margin-bottom: 1rem; - margin-left: 1rem; -} - -/* Custom elements */ - :root { --blue-gray-350: hsl(209deg 13.7% 62.4%); --blue-gray-100: hsl(209.8deg 16.5% 89%); diff --git a/development/ts-migration-dashboard/app/styles/native-elements.scss b/development/ts-migration-dashboard/app/styles/native-elements.scss new file mode 100644 index 000000000..1ef31399d --- /dev/null +++ b/development/ts-migration-dashboard/app/styles/native-elements.scss @@ -0,0 +1,50 @@ +* { + box-sizing: border-box; +} + +html { + font-family: + -apple-system, + BlinkMacSystemFont, + "Segoe UI", + Helvetica, + Arial, + sans-serif, + "Apple Color Emoji", + "Segoe UI Emoji"; + font-size: 16px; +} + +body { + padding: 2rem; +} + +p:not(:last-child) { + margin-bottom: 1rem; +} + +code { + font-size: 0.85em; + font-family: + ui-monospace, + SFMono-Regular, + SF Mono, + Menlo, + Consolas, + Liberation Mono, + monospace; +} + +strong { + font-weight: bold; +} + +em { + font-style: italic; +} + +ul { + list-style: disc; + margin-bottom: 1rem; + margin-left: 1rem; +} diff --git a/development/ts-migration-dashboard/src/tippy.scss b/development/ts-migration-dashboard/app/styles/tippy.scss similarity index 100% rename from development/ts-migration-dashboard/src/tippy.scss rename to development/ts-migration-dashboard/app/styles/tippy.scss diff --git a/development/ts-migration-dashboard/scripts/build-module-partitions.ts b/development/ts-migration-dashboard/common/build-module-partitions.ts similarity index 97% rename from development/ts-migration-dashboard/scripts/build-module-partitions.ts rename to development/ts-migration-dashboard/common/build-module-partitions.ts index 4d4bcc5d8..8ceaff18a 100644 --- a/development/ts-migration-dashboard/scripts/build-module-partitions.ts +++ b/development/ts-migration-dashboard/common/build-module-partitions.ts @@ -3,7 +3,7 @@ import path from 'path'; import fg from 'fast-glob'; import madge from 'madge'; import { - BASE_DIRECTORY, + ROOT_DIRECTORY_PATH, ENTRYPOINT_PATTERNS, FILES_TO_CONVERT_PATH, } from './constants'; @@ -66,7 +66,7 @@ export default async function buildModulePartitions(): Promise< await Promise.all( ENTRYPOINT_PATTERNS.map((entrypointPattern) => { return fg( - path.resolve(BASE_DIRECTORY, `${entrypointPattern}.{js,ts,tsx}`), + path.resolve(ROOT_DIRECTORY_PATH, `${entrypointPattern}.{js,ts,tsx}`), ); }), ) @@ -74,14 +74,14 @@ export default async function buildModulePartitions(): Promise< const entryFilePaths = filterFilePaths( possibleEntryFilePaths.map((possibleEntrypoint) => - path.relative(BASE_DIRECTORY, possibleEntrypoint), + path.relative(ROOT_DIRECTORY_PATH, possibleEntrypoint), ), allowedFilePaths, ); const result = await madge(entryFilePaths, { - baseDir: BASE_DIRECTORY, - tsConfig: path.join(BASE_DIRECTORY, 'tsconfig.json'), + baseDir: ROOT_DIRECTORY_PATH, + tsConfig: path.join(ROOT_DIRECTORY_PATH, 'tsconfig.json'), }); const dependenciesByFilePath = result.obj(); const modulesById = buildModulesWithLevels( diff --git a/development/ts-migration-dashboard/common/constants.ts b/development/ts-migration-dashboard/common/constants.ts new file mode 100644 index 000000000..55cbc98a6 --- /dev/null +++ b/development/ts-migration-dashboard/common/constants.ts @@ -0,0 +1,39 @@ +import path from 'path'; + +export const ROOT_DIRECTORY_PATH = path.resolve(__dirname, '../../..'); +export const PROJECT_DIRECTORY_PATH = path.join( + ROOT_DIRECTORY_PATH, + 'development', + 'ts-migration-dashboard', +); +export const COMMON_DIRECTORY_PATH = path.join( + PROJECT_DIRECTORY_PATH, + 'common', +); +export const APP_DIRECTORY_PATH = path.join(PROJECT_DIRECTORY_PATH, 'app'); +export const INTERMEDIATE_BUILD_DIRECTORY_PATH = path.join( + PROJECT_DIRECTORY_PATH, + 'build', + 'intermediate', +); +export const FINAL_BUILD_DIRECTORY_PATH = path.join( + PROJECT_DIRECTORY_PATH, + 'build', + 'final', +); +export const ENTRYPOINT_PATTERNS = [ + 'app/scripts/background', + 'app/scripts/contentscript', + 'app/scripts/disable-console', + 'app/scripts/inpage', + 'app/scripts/phishing-detect', + 'app/scripts/sentry-install', + 'app/scripts/ui', + 'development/build/index', + '**/*.stories', + '**/*.test', +]; +export const FILES_TO_CONVERT_PATH = path.join( + PROJECT_DIRECTORY_PATH, + 'files-to-convert.json', +); diff --git a/development/ts-migration-dashboard/common/partitions-file.ts b/development/ts-migration-dashboard/common/partitions-file.ts new file mode 100644 index 000000000..e14c9a24f --- /dev/null +++ b/development/ts-migration-dashboard/common/partitions-file.ts @@ -0,0 +1,40 @@ +import { ModulePartition } from './build-module-partitions'; +import { INTERMEDIATE_BUILD_DIRECTORY_PATH } from './constants'; + +// The `brfs` transform for browserify calls `fs.readLineSync` and +// `path.resolve` at build time and inlines file contents into the source code. +// To accomplish this we have to bring in `fs` and `path` using `require` and +// not `import`. This is weird in a TypeScript file, and typescript-eslint +// (rightly) complains about this, but it's actually okay because the above +// `import` lines will actually get turned into `require`s anyway before passing +// through the rest of browserify. However, `brfs` should handle this. There is +// an active bug for this, but there isn't a known workaround yet: +// +/* eslint-disable-next-line @typescript-eslint/no-require-imports,@typescript-eslint/no-var-requires */ +const fs = require('fs'); +/* eslint-disable-next-line @typescript-eslint/no-require-imports,@typescript-eslint/no-var-requires */ +const path = require('path'); + +export const PARTITIONS_FILE = path.join( + INTERMEDIATE_BUILD_DIRECTORY_PATH, + 'partitions.json', +); + +export function readPartitionsFile() { + const content = fs.readFileSync( + // As this function is called within the app code, which is compiled by + // Browserify and not executed by Node, this needs to be here — it cannot be + // extracted — for the reasons explained above. + path.resolve(__dirname, '../build/intermediate/partitions.json'), + { encoding: 'utf-8' }, + ); + return JSON.parse(content) as ModulePartition[]; +} + +export function writePartitionsFile(partitions: ModulePartition[]) { + fs.mkdirSync(path.dirname(PARTITIONS_FILE), { recursive: true }); + return fs.writeFileSync( + PARTITIONS_FILE, + JSON.stringify(partitions, null, ' '), + ); +} diff --git a/development/ts-migration-dashboard/scripts/build.ts b/development/ts-migration-dashboard/scripts/build-app.ts similarity index 76% rename from development/ts-migration-dashboard/scripts/build.ts rename to development/ts-migration-dashboard/scripts/build-app.ts index ebf0bec70..772ce49e1 100644 --- a/development/ts-migration-dashboard/scripts/build.ts +++ b/development/ts-migration-dashboard/scripts/build-app.ts @@ -12,16 +12,19 @@ import gulpDartSass from 'gulp-dart-sass'; import sourcemaps from 'gulp-sourcemaps'; import autoprefixer from 'gulp-autoprefixer'; import fg from 'fast-glob'; -import buildModulePartitions from './build-module-partitions'; +import buildModulePartitions from '../common/build-module-partitions'; +import { + PARTITIONS_FILE, + writePartitionsFile, +} from '../common/partitions-file'; +import { + PROJECT_DIRECTORY_PATH, + COMMON_DIRECTORY_PATH, + APP_DIRECTORY_PATH, + FINAL_BUILD_DIRECTORY_PATH, +} from '../common/constants'; const promisifiedPump = pify(pump); -const projectDirectoryPath = path.resolve(__dirname, '../'); -const sourceDirectoryPath = path.join(projectDirectoryPath, 'src'); -const intermediateDirectoryPath = path.join( - projectDirectoryPath, - 'intermediate', -); -const buildDirectoryPath = path.join(projectDirectoryPath, 'build'); main().catch((error) => { console.error(error); @@ -31,21 +34,15 @@ main().catch((error) => { /** * Compiles a set of files that we want to convert to TypeScript, divided by * level in the dependency tree. - * - * @param dest - The directory in which to hold the file. */ -async function generateIntermediateFiles(dest: string) { +async function generateIntermediateFiles() { const partitions = await buildModulePartitions(); - const partitionsFile = path.resolve(dest, 'partitions.json'); - await pify(fs.writeFile)( - partitionsFile, - JSON.stringify(partitions, null, ' '), - ); + writePartitionsFile(partitions); console.log( `- Wrote intermediate partitions file: ${path.relative( - projectDirectoryPath, - partitionsFile, + PROJECT_DIRECTORY_PATH, + PARTITIONS_FILE, )}`, ); } @@ -81,9 +78,9 @@ async function compileScripts(src: string, dest: string) { console.log( `- Compiled scripts: ${path.relative( - projectDirectoryPath, + PROJECT_DIRECTORY_PATH, src, - )} -> ${path.relative(projectDirectoryPath, dest)}`, + )} -> ${path.relative(PROJECT_DIRECTORY_PATH, dest)}`, ); } @@ -106,9 +103,9 @@ async function compileStylesheets(src: string, dest: string): Promise { ); console.log( `- Compiled stylesheets: ${path.relative( - projectDirectoryPath, + PROJECT_DIRECTORY_PATH, src, - )} -> ${path.relative(projectDirectoryPath, dest)}`, + )} -> ${path.relative(PROJECT_DIRECTORY_PATH, dest)}`, ); } @@ -128,9 +125,9 @@ async function copyStaticFiles(src: string, dest: string): Promise { await fs.copy(srcEntry, destEntry); console.log( `- Copied static files: ${path.relative( - projectDirectoryPath, + PROJECT_DIRECTORY_PATH, srcEntry, - )} -> ${path.relative(projectDirectoryPath, destEntry)}`, + )} -> ${path.relative(PROJECT_DIRECTORY_PATH, destEntry)}`, ); }), ); @@ -160,25 +157,24 @@ async function rebuild({ console.log('Detected change, rebuilding...'); } - await fs.emptyDir(buildDirectoryPath); + await fs.emptyDir(FINAL_BUILD_DIRECTORY_PATH); try { if (isInitial) { - await fs.emptyDir(intermediateDirectoryPath); - await generateIntermediateFiles(intermediateDirectoryPath); + await generateIntermediateFiles(); } await compileScripts( - path.join(sourceDirectoryPath, 'index.tsx'), - path.join(buildDirectoryPath, 'index.js'), + path.join(APP_DIRECTORY_PATH, 'index.tsx'), + path.join(FINAL_BUILD_DIRECTORY_PATH, 'index.js'), ); await compileStylesheets( - path.join(sourceDirectoryPath, 'index.scss'), - path.join(buildDirectoryPath), + path.join(APP_DIRECTORY_PATH, 'index.scss'), + FINAL_BUILD_DIRECTORY_PATH, ); await copyStaticFiles( - path.join(sourceDirectoryPath, 'public'), - path.join(buildDirectoryPath), + path.join(APP_DIRECTORY_PATH, 'public'), + FINAL_BUILD_DIRECTORY_PATH, ); } catch (error: unknown) { console.error(error); @@ -202,7 +198,7 @@ async function main() { .alias('h', 'help') .parseSync(); - console.log(`Working directory: ${projectDirectoryPath}`); + console.log(`Working directory: ${PROJECT_DIRECTORY_PATH}`); if (opts.watch) { const rebuildIgnoringErrors = () => { @@ -211,9 +207,15 @@ async function main() { }); }; chokidar - .watch(path.join(sourceDirectoryPath, '**/*.{html,ts,tsx,scss}'), { - ignoreInitial: true, - }) + .watch( + [ + path.join(COMMON_DIRECTORY_PATH, '**/*.{html,ts,tsx,scss}'), + path.join(APP_DIRECTORY_PATH, '**/*.{html,ts,tsx,scss}'), + ], + { + ignoreInitial: true, + }, + ) .on('add', rebuildIgnoringErrors) .on('change', rebuildIgnoringErrors) .on('unlink', rebuildIgnoringErrors) diff --git a/development/ts-migration-dashboard/scripts/constants.ts b/development/ts-migration-dashboard/scripts/constants.ts deleted file mode 100644 index a283fd8e0..000000000 --- a/development/ts-migration-dashboard/scripts/constants.ts +++ /dev/null @@ -1,19 +0,0 @@ -import path from 'path'; - -export const BASE_DIRECTORY = path.resolve(__dirname, '../../..'); -export const ENTRYPOINT_PATTERNS = [ - 'app/scripts/background', - 'app/scripts/contentscript', - 'app/scripts/disable-console', - 'app/scripts/inpage', - 'app/scripts/phishing-detect', - 'app/scripts/sentry-install', - 'app/scripts/ui', - 'development/build/index', - '**/*.stories', - '**/*.test', -]; -export const FILES_TO_CONVERT_PATH = path.join( - __dirname, - '../files-to-convert.json', -); diff --git a/development/ts-migration-dashboard/scripts/write-list-of-files-to-convert.ts b/development/ts-migration-dashboard/scripts/write-list-of-files-to-convert.ts index cd375ca9d..ead0cb218 100644 --- a/development/ts-migration-dashboard/scripts/write-list-of-files-to-convert.ts +++ b/development/ts-migration-dashboard/scripts/write-list-of-files-to-convert.ts @@ -3,10 +3,10 @@ import fs from 'fs'; import fg from 'fast-glob'; import madge from 'madge'; import { - BASE_DIRECTORY, + ROOT_DIRECTORY_PATH, ENTRYPOINT_PATTERNS, FILES_TO_CONVERT_PATH, -} from './constants'; +} from '../common/constants'; main().catch((error) => { console.error(error); @@ -26,7 +26,7 @@ async function main(): Promise { await Promise.all( ENTRYPOINT_PATTERNS.map((entrypointPattern) => { return fg( - path.resolve(BASE_DIRECTORY, `${entrypointPattern}.{js,ts,tsx}`), + path.resolve(ROOT_DIRECTORY_PATH, `${entrypointPattern}.{js,ts,tsx}`), ); }), ) @@ -35,7 +35,7 @@ async function main(): Promise { `Traversing dependency trees for ${entrypoints.length} entrypoints, please wait...`, ); const result = await madge(entrypoints, { - baseDir: BASE_DIRECTORY, + baseDir: ROOT_DIRECTORY_PATH, }); const dependenciesByFilePath = result.obj(); const sortedFilePaths = Object.keys(dependenciesByFilePath) diff --git a/package.json b/package.json index 5b254f742..3e830d188 100644 --- a/package.json +++ b/package.json @@ -83,10 +83,10 @@ "lavamoat:webapp:auto:ci": "node ./development/generate-lavamoat-policies.js --parallel=false", "lavamoat:auto": "yarn lavamoat:build:auto && yarn lavamoat:webapp:auto", "lavamoat:auto:ci": "yarn lavamoat:build:auto && yarn lavamoat:webapp:auto:ci", + "ts-migration:dashboard:build": "ts-node development/ts-migration-dashboard/scripts/build-app.ts", + "ts-migration:dashboard:deploy": "gh-pages --dist development/ts-migration-dashboard/build/final --remote ts-migration-dashboard", + "ts-migration:dashboard:watch": "yarn ts-migration:dashboard:build --watch", "ts-migration:enumerate": "ts-node development/ts-migration-dashboard/scripts/write-list-of-files-to-convert.ts", - "ts-migration:dashboard:watch": "ts-node development/ts-migration-dashboard/scripts/build.ts --watch", - "ts-migration:dashboard:build": "ts-node development/ts-migration-dashboard/scripts/build.ts", - "ts-migration:dashboard:deploy": "gh-pages --dist development/ts-migration-dashboard/build --remote ts-migration-dashboard", "test-storybook": "test-storybook -c .storybook", "test-storybook:ci": "concurrently -k -s first -n \"SB,TEST\" -c \"magenta,blue\" \"yarn storybook:build && npx http-server storybook-build --port 6006 \" \"wait-on tcp:6006 && yarn test-storybook --maxWorkers=2\"" },