diff --git a/package.json b/package.json index b228f0ce812d22..d14336462d5be9 100644 --- a/package.json +++ b/package.json @@ -124,6 +124,7 @@ "@testing-library/user-event": "13.5.0", "@tsconfig/node14": "1.0.3", "@types/babel__helper-plugin-utils": "7.10.0", + "@types/babel__register": "7.17.0", "@types/chrome-remote-interface": "0.30.0", "@types/circular-dependency-plugin": "5.0.5", "@types/copy-webpack-plugin": "6.4.0", @@ -135,6 +136,7 @@ "@types/express": "4.17.2", "@types/fs-extra": "8.0.1", "@types/glob": "7.1.1", + "@types/graphviz": "0.0.34", "@types/gulp": "4.0.9", "@types/gulp-babel": "6.1.30", "@types/gulp-cache": "0.4.5", @@ -161,6 +163,7 @@ "@types/scheduler": "0.16.2", "@types/semver": "^6.2.0", "@types/tmp": "0.2.0", + "@types/webpack-bundle-analyzer": "4.4.3", "@types/webpack-dev-middleware": "4.1.0", "@types/webpack-env": "1.16.0", "@types/webpack-hot-middleware": "2.25.6", @@ -193,6 +196,7 @@ "danger": "^11.0.0", "dedent": "0.7.0", "del": "6.0.0", + "dotparser": "1.1.1", "enhanced-resolve": "5.8.2", "enquirer": "2.3.6", "enzyme": "3.10.0", @@ -215,9 +219,11 @@ "extract-comments": "1.1.0", "file-loader": "6.2.0", "fork-ts-checker-webpack-plugin": "6.1.0", + "find-free-port": "2.0.0", "fs-extra": "8.1.0", "geckodriver": "3.0.2", "glob": "7.2.0", + "graphviz": "0.0.9", "gulp": "4.0.2", "gulp-babel": "8.0.0", "gulp-cache": "1.1.3", diff --git a/scripts/api-extractor/tsconfig.json b/scripts/api-extractor/tsconfig.json new file mode 100644 index 00000000000000..0c85a908fa4a5a --- /dev/null +++ b/scripts/api-extractor/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../tsconfig.scripts.json", + "compilerOptions": { + "types": ["node"] + }, + "include": ["*"] +} diff --git a/scripts/babel/index.js b/scripts/babel/index.js index a9891e8ad9ce1d..7363e8579669ec 100644 --- a/scripts/babel/index.js +++ b/scripts/babel/index.js @@ -1,14 +1,17 @@ -const isNodeCaller = caller => { - return caller && (caller.name === '@babel/register' || caller.name === 'babel-jest'); +/** @typedef {import('@babel/core').TransformOptions['caller']} Caller */ + +const isNodeCaller = (/** @type {Caller}*/ caller) => { + return Boolean(caller && (caller.name === '@babel/register' || caller.name === 'babel-jest')); }; -const isDistCaller = caller => { +const isDistCaller = (/** @type {Caller}*/ caller) => { return !!(caller && caller.name === 'babel-gulp'); }; -const supportsESM = caller => { +const supportsESM = (/** @type {Caller}*/ caller) => { + // @ts-expect-error - TODO: caller.useESModules api doesn't exists (types/implementation), is this a bug? https://babeljs.io/docs/en/options#caller return !!((caller && caller.name === 'babel-loader') || caller.useESModules); }; -module.exports = api => { +module.exports = (/** @type {import('@babel/core').ConfigAPI} */ api) => { const isDistBundle = api.caller(isDistCaller); const isNode = api.caller(isNodeCaller); const useESModules = !isNode && api.caller(supportsESM); diff --git a/scripts/babel/register.js b/scripts/babel/register.js index ef9066528e851e..9deba5b495e062 100644 --- a/scripts/babel/register.js +++ b/scripts/babel/register.js @@ -1,4 +1,3 @@ -// @ts-ignore - @babel/register doesn't ship types require('@babel/register')({ extensions: ['.js', '.ts', '.tsx'], ignore: [/node_modules/], diff --git a/scripts/babel/tsconfig.json b/scripts/babel/tsconfig.json new file mode 100644 index 00000000000000..0c85a908fa4a5a --- /dev/null +++ b/scripts/babel/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../tsconfig.scripts.json", + "compilerOptions": { + "types": ["node"] + }, + "include": ["*"] +} diff --git a/scripts/dependency-graph-generator/index.js b/scripts/dependency-graph-generator/index.js index c729885139bbe0..fecb8a16bb5539 100644 --- a/scripts/dependency-graph-generator/index.js +++ b/scripts/dependency-graph-generator/index.js @@ -1,12 +1,18 @@ const graphviz = require('graphviz'); -const parser = require('dotparser'); -const readFileSync = require('fs').readFileSync; -const spawnSync = require('child_process').spawnSync; -const findGitRoot = require('../monorepo/index').findGitRoot; -const getDependencies = require('../monorepo/index').getDependencies; +const parser = /** @type {typeof import('dotparser')['default']}*/ (/** @type {unknown} */ (require('dotparser'))); +const { readFileSync } = require('fs'); +const { spawnSync } = require('child_process'); const path = require('path'); +const yargs = require('yargs'); + +/** @typedef {import('yargs').Arguments<{root:string;'include-dev'?:string;'graphviz-path'?:string}>} CliArgs */ + +const { findGitRoot, getDependencies } = require('../monorepo'); const dotFilePath = path.resolve(__dirname, 'repo-graph.dot'); +/** + * @type {string[]} + */ let ignoreDevDependencies = []; /** @@ -14,6 +20,7 @@ let ignoreDevDependencies = []; * * For this utility to work you will actually need to install graphviz on your machine * http://www.graphviz.org + * @param {CliArgs} argv */ async function main(argv) { if (!argv.root) { @@ -81,8 +88,9 @@ function _parseDotFile(pathToDotFile) { const items = parsed[0].children; items.forEach(item => { if (item.type === 'node_stmt') { - graph.addNode(item.node_id.id); + graph.addNode(String(item.node_id.id)); } else { + // @ts-expect-error - edge_list doesn't exist on type definition - is this a bug ? graph.addEdge(item.edge_list[0].id, item.edge_list[1].id, { dir: 'forward' }); } }); @@ -130,8 +138,11 @@ function _getSubTree(graph, rootPackage) { * @param {string} node id of the node */ function _getEdgesAndChildren(graph, node) { + /** @type any[] */ const edges = []; + /** @type any[] */ const children = []; + // @ts-expect-error - edges prop doesn't exist on typings - wrong typings ? graph.edges.forEach(edge => { if (ignoreDevDependencies.includes(edge.nodeOne.id) || ignoreDevDependencies.includes(edge.nodeTwo.id)) { return; @@ -146,7 +157,7 @@ function _getEdgesAndChildren(graph, node) { return { edges, children }; } -require('yargs') +yargs .scriptName('dependency-graph-generator') .usage('$0 [args]') .command( @@ -168,7 +179,7 @@ require('yargs') description: 'The path to graphviz which needs to be installed, required for windows users', }); }, - async argv => await main(argv), + async argv => await main(/** @type {CliArgs}*/ (argv)), ) .demand('root') .help().argv; diff --git a/scripts/dependency-graph-generator/tsconfig.json b/scripts/dependency-graph-generator/tsconfig.json new file mode 100644 index 00000000000000..0c85a908fa4a5a --- /dev/null +++ b/scripts/dependency-graph-generator/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../tsconfig.scripts.json", + "compilerOptions": { + "types": ["node"] + }, + "include": ["*"] +} diff --git a/scripts/jest/index.js b/scripts/jest/index.js index a23a4265106eb3..13768877335f77 100644 --- a/scripts/jest/index.js +++ b/scripts/jest/index.js @@ -1,8 +1,7 @@ -// @ts-check const lernaAlias = require('../lernaAliasNorthstar'); const findGitRoot = require('../monorepo/findGitRoot'); -module.exports = customConfig => ({ +module.exports = (/** @type {import('@jest/types').Config.InitialOptions} */ customConfig) => ({ coverageDirectory: './coverage/', coverageReporters: ['json', 'lcov'], moduleFileExtensions: ['ts', 'tsx', 'js', 'json'], diff --git a/scripts/jest/jest-reporter.js b/scripts/jest/jest-reporter.js index 9780b0b02f6120..edffa1d8c95d88 100644 --- a/scripts/jest/jest-reporter.js +++ b/scripts/jest/jest-reporter.js @@ -13,7 +13,7 @@ class JestReporter extends DefaultReporter { super(...args); this._isLoggingError = false; - this.log = message => { + this.log = (/** @type {string} */ message) => { if (this._isLoggingError) { process.stderr.write(message + '\n'); } else { diff --git a/scripts/jest/jest-resources.js b/scripts/jest/jest-resources.js index 9c21cf68ab8d01..4f5b9c10100df0 100644 --- a/scripts/jest/jest-resources.js +++ b/scripts/jest/jest-resources.js @@ -1,20 +1,20 @@ -// @ts-check - const fs = require('fs-extra'); const path = require('path'); -const merge = require('../tasks/merge'); const resolve = require('resolve'); const { resolveCwd } = require('just-scripts'); -const { findRepoDeps } = require('../monorepo/index'); + +const merge = require('../tasks/merge'); +const { findRepoDeps } = require('../monorepo'); const findConfig = require('../find-config'); -const packageJsonPath = findConfig('package.json'); +const packageJsonPath = findConfig('package.json') ?? ''; const packageRoot = path.dirname(packageJsonPath); const jestAliases = () => { // Get deps of the current package within the repo const packageDeps = findRepoDeps(); + /** @type {Record} */ const aliases = {}; for (const { packageJson } of packageDeps) { diff --git a/scripts/jest/jest-setup.js b/scripts/jest/jest-setup.js index 2746eb3135b53a..9a83be192a6e5d 100644 --- a/scripts/jest/jest-setup.js +++ b/scripts/jest/jest-setup.js @@ -19,6 +19,11 @@ const consoleError = console.error; console.error = customError.bind(null, 'error'); console.warn = customError.bind(null, 'warn'); +/** + * + * @param {string} type + * @param {any[]} args + */ function customError(type, ...args) { if (type === 'warn') { consoleWarn(...args); diff --git a/scripts/jest/tsconfig.json b/scripts/jest/tsconfig.json new file mode 100644 index 00000000000000..4031d4e78fd112 --- /dev/null +++ b/scripts/jest/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../tsconfig.scripts.json", + "compilerOptions": { + "types": ["node", "jest"] + }, + "include": ["*"] +} diff --git a/scripts/logging.js b/scripts/logging.js index dec1e9d5b59bcc..46c8d6556ab522 100644 --- a/scripts/logging.js +++ b/scripts/logging.js @@ -3,10 +3,22 @@ const chalk = require('chalk'); const isProduction = process.argv.indexOf('--production') > -1; const isVerbose = process.argv.indexOf('--verbose') > -1; +/** + * + * @param {string} packageName + * @param {string} task + */ module.exports.logStartTask = (packageName, task) => { console.log(`${getTimePrefix(packageName)} Starting: ${chalk.cyan(task)}`); }; +/** + * + * @param {string} packageName + * @param {string} task + * @param {number} startTime + * @param {string} errorMessage + */ module.exports.logEndTask = (packageName, task, startTime, errorMessage) => { console.log( `${getTimePrefix(packageName)} ${getPassFail(errorMessage === undefined)}: ${chalk.cyan(task)} (${getDuration( @@ -15,12 +27,22 @@ module.exports.logEndTask = (packageName, task, startTime, errorMessage) => { ); }; +/** + * + * @param {unknown} taskStatus + */ module.exports.logStatus = taskStatus => { if (isProduction || isVerbose) { console.log(' ' + taskStatus); } }; +/** + * + * @param {string} packageName + * @param {boolean} passed + * @param {number} startTime + */ module.exports.logEndBuild = (packageName, passed, startTime) => { console.log(); console.log( @@ -43,19 +65,40 @@ module.exports.logEndBuild = (packageName, passed, startTime) => { ); }; +/** + * + * @param {number} startTime + * @returns + */ function getDuration(startTime) { let duration = new Date().getTime() - startTime; return chalk.yellow(formatTime(duration)); } + +/** + * + * @param {boolean} passed + * @returns + */ function getPassFail(passed) { return passed ? chalk.green('Pass') : chalk.red('Error'); } +/** + * + * @param {string} packageName + * @returns + */ function getTimePrefix(packageName) { return `[${chalk.magenta(packageName)} ${chalk.gray(new Date().toLocaleTimeString([], { hour12: false }))}]`; } +/** + * + * @param {number} milliseconds + * @returns + */ function formatTime(milliseconds) { if (milliseconds >= 1000) { return milliseconds / 1000 + 's'; diff --git a/scripts/package.json b/scripts/package.json index e8890ba34e7595..85da64548204a3 100644 --- a/scripts/package.json +++ b/scripts/package.json @@ -27,9 +27,6 @@ "connect-history-api-fallback": "^1.3.0", "doctoc": "^2.0.1", "doctrine": "^3.0.0", - "dotparser": "^1.0.0", - "find-free-port": "~2.0.0", - "graphviz": "^0.0.9", "inquirer": "^7.3.3", "lerna-alias": "^3.0.3-0", "lerna-dependency-graph": "^1.0.2", diff --git a/scripts/tasks/api-extractor.ts b/scripts/tasks/api-extractor.ts index dcebe9becf3ee4..109d138cbdb6e8 100644 --- a/scripts/tasks/api-extractor.ts +++ b/scripts/tasks/api-extractor.ts @@ -112,7 +112,7 @@ export function apiExtractor() { } }, onConfigLoaded: config => { - if (!isUsingTsSolutionConfigs) { + if (!(isUsingTsSolutionConfigs && tsConfig)) { return; } diff --git a/scripts/tasks/argv.ts b/scripts/tasks/argv.ts index 96c186882a93ba..f91f77e4f91b73 100644 --- a/scripts/tasks/argv.ts +++ b/scripts/tasks/argv.ts @@ -36,7 +36,7 @@ function parseModule(args: Arguments & { module?: string | string[] }) { ); } - acc[outputType] = true; + acc[outputType as keyof JustArgs['module']] = true; return acc; }, diff --git a/scripts/tasks/babel.ts b/scripts/tasks/babel.ts index 5ab16fc7575ec4..34be991fa02721 100644 --- a/scripts/tasks/babel.ts +++ b/scripts/tasks/babel.ts @@ -1,4 +1,4 @@ -import { transformAsync } from '@babel/core'; +import { BabelFileResult, transformAsync } from '@babel/core'; import * as glob from 'glob'; import fs from 'fs'; import { logger } from 'just-task'; @@ -20,7 +20,7 @@ export async function babel() { const codeBuffer = await fs.promises.readFile(filePath); const sourceCode = codeBuffer.toString().replace(EOL_REGEX, '\n'); - const result = await transformAsync(sourceCode, { + const result = (await transformAsync(sourceCode, { ast: false, sourceMaps: true, @@ -32,7 +32,7 @@ export async function babel() { filename: filePath, sourceFileName: path.basename(filename), - }); + })) /* Bad `transformAsync` types. it can be null only if 2nd param is null(config)*/ as NonNullableRecord; const resultCode = addSourceMappingUrl(result.code, path.basename(filename) + '.map'); if (resultCode === sourceCode) { @@ -48,3 +48,7 @@ export async function babel() { await fs.promises.writeFile(sourceMapFile, JSON.stringify(result.map)); } } + +type NonNullableRecord = { + [P in keyof T]-?: NonNullable; +}; diff --git a/scripts/tasks/bundle-size-collect.ts b/scripts/tasks/bundle-size-collect.ts index 6b1ea46dbed94a..ebac469be4393f 100644 --- a/scripts/tasks/bundle-size-collect.ts +++ b/scripts/tasks/bundle-size-collect.ts @@ -10,7 +10,7 @@ export function bundleSizeCollect() { // information which gets reported by Size Auditor const distRoot = path.join(__dirname, '../../apps/test-bundles/dist'); - const sizes = {}; + const sizes: Record = {}; const outputFilename = 'bundlesize.json'; var items = fs.readdirSync(distRoot); @@ -25,11 +25,11 @@ export function bundleSizeCollect() { fs.writeFileSync(path.join(distRoot, outputFilename), JSON.stringify({ sizes })); - function getFilesizeInBytes(fileName) { + function getFilesizeInBytes(fileName: string) { return fs.statSync(fileName).size; } - function getComponentName(fileName) { + function getComponentName(fileName: string) { return path.basename(fileName, '.min.js'); } } diff --git a/scripts/tasks/copy.ts b/scripts/tasks/copy.ts index d4a5035f3f8e48..b90aee3bc6fdb6 100644 --- a/scripts/tasks/copy.ts +++ b/scripts/tasks/copy.ts @@ -4,7 +4,7 @@ import { series, resolveCwd, copyTask, copyInstructionsTask, logger } from 'just import { getProjectMetadata, findGitRoot } from '../monorepo'; import { getTsPathAliasesConfig } from './utils'; -export function expandSourcePath(pattern: string): string { +export function expandSourcePath(pattern: string): string | null { if (!pattern) { return null; } @@ -29,6 +29,7 @@ export function expandSourcePath(pattern: string): string { return pattern.replace(packageName, path.dirname(resolvedPackageJson)); } catch (e) { console.error(e); + return null; } } @@ -42,7 +43,7 @@ export function copyCompiled() { const packageDir = process.cwd(); - if (!isUsingTsSolutionConfigs) { + if (!(isUsingTsSolutionConfigs && tsConfig)) { throw new Error(`this task compliant only with packages that use TS solution config files.`); } @@ -57,6 +58,13 @@ export function copyCompiled() { const projectMetadata = getProjectMetadata({ root, name: packageJson.name }); + if (!projectMetadata.sourceRoot) { + throw new Error(`${packageJson.name} is missing 'sourceRoot' in workspace.json`); + } + if (!packageJson.module) { + throw new Error(`${packageJson.name} is missing 'module' property in package.json`); + } + const paths = { esm: { in: path.join( @@ -110,7 +118,7 @@ export function copy() { if (Array.isArray(sources)) { return copyTask({ - paths: sources.map(src => expandSourcePath(src)), + paths: sources.map(src => expandSourcePath(src)).filter(Boolean) as string[], dest: destinationPath, }); } diff --git a/scripts/tasks/generate-version-files.ts b/scripts/tasks/generate-version-files.ts index c8d7054b392613..f0ffc54a6d1580 100644 --- a/scripts/tasks/generate-version-files.ts +++ b/scripts/tasks/generate-version-files.ts @@ -9,7 +9,7 @@ const beachballBin = require.resolve('beachball/bin/beachball.js'); const bumpCmd = [process.execPath, beachballBin, 'bump']; const gitRoot = findGitRoot(); -function run(args) { +function run(args: string[]) { const [cmd, ...restArgs] = args; const runResult = spawnSync(cmd, restArgs, { cwd: gitRoot }); if (runResult.status === 0) { @@ -59,7 +59,7 @@ export function generateVersionFiles() { } // 2. gather version info - const updatedVersionContents = {}; + const updatedVersionContents: Record = {}; const packageJsons = glob.sync('+(packages|apps)/*/package.json', { cwd: gitRoot }); packageJsons.forEach(packageJsonPath => { const versionFile = path.join(gitRoot, path.dirname(packageJsonPath), 'src/version.ts'); diff --git a/scripts/tasks/lint-imports.ts b/scripts/tasks/lint-imports.ts index 6dab2dda2092af..8bdd6c1bf3ef09 100644 --- a/scripts/tasks/lint-imports.ts +++ b/scripts/tasks/lint-imports.ts @@ -1,4 +1,4 @@ -import { getAllPackageInfo, findGitRoot } from '../monorepo/index'; +import { getAllPackageInfo, findGitRoot } from '../monorepo'; import { readConfig } from '../read-config'; import * as glob from 'glob'; import * as path from 'path'; @@ -119,7 +119,7 @@ export function lintImports() { if (!exportStatements) { _addError(importErrors.exportMulti, relativePath, 'no exported class or const'); } else if (exportStatements.length > 1) { - const exports = exportStatements.map(exp => exp.match(exportRegex)[2]); + const exports = exportStatements.map(exp => (exp.match(exportRegex) as RegExpMatchArray)[2]); _addError(importErrors.exportMulti, relativePath, 'choose one of ' + exports.join(', ')); } @@ -144,10 +144,10 @@ export function lintImports() { const importPath = importMatch[2]; const packageRootPath = importPath.split('/')[0]; const relativePath = path.relative(sourcePath, filePath); - let fullImportPath: string; + let fullImportPath: string | undefined; let pathIsRelative = false; let pathIsDeep = false; - let pkgName: string; + let pkgName: string | undefined = undefined; if (importPath[0] === '.') { // import is a file path. is this a file? @@ -211,12 +211,15 @@ export function lintImports() { _addError(importErrors.pathDeep, relativePath, importPath); } - if (reExportedPackages[pkgName] && !allowedReexportedImports.includes(importPath)) { + if ( + reExportedPackages[pkgName as keyof typeof reExportedPackages] && + !allowedReexportedImports.includes(importPath) + ) { _addError( importErrors.pathReExported, relativePath, importPath, - '@fluentui/react/lib/' + reExportedPackages[pkgName], + '@fluentui/react/lib/' + reExportedPackages[pkgName as keyof typeof reExportedPackages], ); } @@ -238,7 +241,7 @@ export function lintImports() { } } - return undefined; + return; } function _addError(errorGroup: ImportErrorGroup, relativePath: string, importPath: string, alternative?: string) { @@ -270,7 +273,7 @@ export function lintImports() { }; let hasError = false; - for (const groupName of Object.keys(importErrors)) { + for (const groupName of Object.keys(importErrors) as Array) { const errorGroup: ImportErrorGroup = importErrors[groupName]; if (errorGroup.count) { hasError = true; diff --git a/scripts/tasks/merge.js b/scripts/tasks/merge.js index c8c6846bd80038..5b5f94f30cf997 100644 --- a/scripts/tasks/merge.js +++ b/scripts/tasks/merge.js @@ -1,4 +1,11 @@ -module.exports = function merge(obj1, obj2) { +module.exports = merge; + +/** + * + * @param {Record} obj1 + * @param {Record} obj2 + */ +function merge(obj1, obj2) { const merged = Object.assign({}, obj1); for (const prop in obj2) { @@ -15,4 +22,4 @@ module.exports = function merge(obj1, obj2) { } return merged; -}; +} diff --git a/scripts/tasks/sass.ts b/scripts/tasks/sass.ts index 999501e00b779c..b247098714619b 100644 --- a/scripts/tasks/sass.ts +++ b/scripts/tasks/sass.ts @@ -3,7 +3,7 @@ import * as glob from 'glob'; import { sassTask } from 'just-scripts'; import postcssModules from 'postcss-modules'; -const _fileNameToClassMap = {}; +const _fileNameToClassMap: Record> = {}; function createTypeScriptModule(fileName: string, css: string) { const { splitStyles } = require('@microsoft/load-themed-styles'); diff --git a/scripts/tasks/storybook.ts b/scripts/tasks/storybook.ts index 4ee09dd372512d..cc141060edabcb 100644 --- a/scripts/tasks/storybook.ts +++ b/scripts/tasks/storybook.ts @@ -1,9 +1,14 @@ import { argv } from 'just-scripts'; import * as fs from 'fs'; import * as path from 'path'; -import { findGitRoot } from '../monorepo/index'; +import { findGitRoot } from '../monorepo'; -import storybook from '@storybook/react/standalone'; +// TODO: this should be replaced with `'@storybook/core-server';` API +// @ts-expect-error - standalone.js is basically private API/thus doesn't ship types. NOTE: Storybook can be run standalone from Node, although it should be noted this isn't officially supported any more. - https://github.com/storybookjs/storybook/blob/master/lib/core/docs/standalone.md#standalone-mode +import _storybook from '@storybook/react/standalone'; +const storybook: StorybookStandaloneBuildFn = _storybook; + +type StorybookStandaloneBuildFn = (options?: StorybookDevOptions | StorybookStaticOptions) => Promise; // Option types are documented here but not included in package for some reason // https://github.com/storybookjs/storybook/blob/master/lib/core/docs/standalone.md diff --git a/scripts/tasks/ts.ts b/scripts/tasks/ts.ts index 652f96151fcdc2..30a3e54b941dfc 100644 --- a/scripts/tasks/ts.ts +++ b/scripts/tasks/ts.ts @@ -24,7 +24,7 @@ function prepareTsTaskConfig(options: TscTaskOptions) { const { isUsingTsSolutionConfigs, tsConfigFile, tsConfig } = getTsPathAliasesConfig(); - if (isUsingTsSolutionConfigs) { + if (isUsingTsSolutionConfigs && tsConfig) { logger.info(`📣 TSC: package is using TS path aliases. Overriding tsconfig settings.`); const tsConfigOutDir = tsConfig.compilerOptions.outDir as string; diff --git a/scripts/tasks/tsconfig.json b/scripts/tasks/tsconfig.json new file mode 100644 index 00000000000000..9dc1fd8575760c --- /dev/null +++ b/scripts/tasks/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../tsconfig.scripts.json", + "compilerOptions": { + "types": ["node", "jest"] + }, + "include": ["*"], + "files": ["../../typings/find-free-port/index.d.ts"] +} diff --git a/scripts/tsconfig.json b/scripts/tsconfig.json index 923f9a4c7d0d98..07ba9e5936b901 100644 --- a/scripts/tsconfig.json +++ b/scripts/tsconfig.json @@ -24,6 +24,15 @@ "./prettier/**", "./fluentui-publish/**", "./puppeteer/**", - "./triage-bot/**" + "./triage-bot/**", + "./webpack/**", + "./jest/**", + "./dependency-graph-generator/**", + "./typescript/**", + "./tasks/**", + "./babel/**", + "./api-extractor/**", + "./typescript/**", + "./lint-staged/**" ] } diff --git a/scripts/tsconfig.scripts.json b/scripts/tsconfig.scripts.json index c87a04ae385929..7c707022600a74 100644 --- a/scripts/tsconfig.scripts.json +++ b/scripts/tsconfig.scripts.json @@ -11,6 +11,30 @@ "include": [], "files": [], "references": [ + { + "path": "./api-extractor/tsconfig.json" + }, + { + "path": "./babel/tsconfig.json" + }, + { + "path": "./lint-staged/tsconfig.json" + }, + { + "path": "./typescript/tsconfig.json" + }, + { + "path": "./tasks/tsconfig.json" + }, + { + "path": "./jest/tsconfig.json" + }, + { + "path": "./webpack/tsconfig.json" + }, + { + "path": "./dependency-graph-generator/tsconfig.json" + }, { "path": "./triage-bot/tsconfig.json" }, diff --git a/scripts/typescript/normalize-import.js b/scripts/typescript/normalize-import.js index 0cd5a0c1fd69af..602dedbee3dc5a 100644 --- a/scripts/typescript/normalize-import.js +++ b/scripts/typescript/normalize-import.js @@ -1,5 +1,3 @@ -// @ts-check - const fs = require('fs'); const path = require('path'); const chalk = require('chalk'); diff --git a/scripts/typescript/normalize-import.spec.js b/scripts/typescript/normalize-import.spec.js index bc44796963d5a4..f82b733a94ac8b 100644 --- a/scripts/typescript/normalize-import.spec.js +++ b/scripts/typescript/normalize-import.spec.js @@ -1,5 +1,3 @@ -// @ts-check - const { execSync } = require('child_process'); const fs = require('fs'); const path = require('path'); diff --git a/scripts/typescript/tsconfig.json b/scripts/typescript/tsconfig.json new file mode 100644 index 00000000000000..4031d4e78fd112 --- /dev/null +++ b/scripts/typescript/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../tsconfig.scripts.json", + "compilerOptions": { + "types": ["node", "jest"] + }, + "include": ["*"] +} diff --git a/scripts/webpack/getDefaultEnvironmentVars.js b/scripts/webpack/getDefaultEnvironmentVars.js index 12c0a198e613ba..44b787e549829d 100644 --- a/scripts/webpack/getDefaultEnvironmentVars.js +++ b/scripts/webpack/getDefaultEnvironmentVars.js @@ -9,10 +9,11 @@ * Helper which resolves a specific set of environment variable values. If not present, * the provided default values will be returned. * - * @param options - A object where the keys are the environment variables to read, + * @param {Record} options - A object where the keys are the environment variables to read, * and their values represent the default value to use if the variable is not present. */ const getVariables = options => { + /** @type {Record} */ const variables = {}; for (const key of Object.keys(options)) { @@ -28,9 +29,9 @@ const getVariables = options => { * Function which returns DefinePlugin options for a specific set of environment variables. * This is needed because Webpack 5 no longer automatically resolves process.env values. * - * @param {boolean=} isProduction - If true will ensure NODE_ENV is 'production', even + * @param {boolean} [isProduction] - If true will ensure NODE_ENV is 'production', even * if environment variables specify otherwise. - * @param {object=} otherValues - Other values to include in the environment. Key is the + * @param {Record} [otherValues] - Other values to include in the environment. Key is the * environment variable name and value is the non-stringified value. */ module.exports = (isProduction, otherValues) => ({ diff --git a/scripts/webpack/getResolveAlias.js b/scripts/webpack/getResolveAlias.js index 6afd0c7b2ac10c..d96cc20127b803 100644 --- a/scripts/webpack/getResolveAlias.js +++ b/scripts/webpack/getResolveAlias.js @@ -1,7 +1,6 @@ -// @ts-check const path = require('path'); -const { findRepoDeps, findGitRoot } = require('../monorepo/index'); +const { findRepoDeps, findGitRoot } = require('../monorepo'); const { readConfig } = require('../read-config'); /** @@ -87,7 +86,6 @@ function getResolveAlias(useLib, cwd) { module.exports = getResolveAlias; -// @ts-ignore if (require.main === module) { console.log(getResolveAlias()); } diff --git a/scripts/webpack/tsconfig.json b/scripts/webpack/tsconfig.json new file mode 100644 index 00000000000000..0c85a908fa4a5a --- /dev/null +++ b/scripts/webpack/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../tsconfig.scripts.json", + "compilerOptions": { + "types": ["node"] + }, + "include": ["*"] +} diff --git a/scripts/webpack/webpack-resources.js b/scripts/webpack/webpack-resources.js index d07f8afd746268..79451d435de394 100644 --- a/scripts/webpack/webpack-resources.js +++ b/scripts/webpack/webpack-resources.js @@ -1,23 +1,21 @@ -// @ts-check - /** * @typedef {import("webpack").Configuration} WebpackConfig * @typedef {WebpackConfig & { devServer?: object }} WebpackServeConfig * @typedef {import("webpack").ModuleOptions} WebpackModule * @typedef {import("webpack").Configuration['output']} WebpackOutput */ -/** */ + const webpack = require('webpack'); const path = require('path'); const fs = require('fs'); -/** @type {(c1: Partial, c2: Partial) => WebpackServeConfig} */ -const merge = require('../tasks/merge'); const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin'); +// @ts-ignore - accessing package.json is a private API access, thus ignoring TS here +const webpackVersion = /** @type {string} */ (require('webpack/package.json').version); + +const merge = require('../tasks/merge'); +const { findGitRoot } = require('../monorepo'); const getResolveAlias = require('./getResolveAlias'); -const { findGitRoot } = require('../monorepo/index'); -// @ts-ignore -const webpackVersion = require('webpack/package.json').version; const getDefaultEnvironmentVars = require('./getDefaultEnvironmentVars'); console.log(`Webpack version: ${webpackVersion}`); @@ -318,8 +316,16 @@ module.exports = { }, }; +/** + * + * @param {string} bundleName + * @param {boolean} isProduction + * @param {boolean} [profile] + * @returns + */ function getPlugins(bundleName, isProduction, profile) { const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; + /** @type {webpack.WebpackPluginInstance[]} */ const plugins = [new webpack.DefinePlugin(getDefaultEnvironmentVars(isProduction))]; if (isProduction && profile) { diff --git a/scripts/webpack/webpack.config.stats.ts b/scripts/webpack/webpack.config.stats.ts index cca7e39792c117..27dcf8ea167fa6 100644 --- a/scripts/webpack/webpack.config.stats.ts +++ b/scripts/webpack/webpack.config.stats.ts @@ -15,18 +15,19 @@ const { paths } = config; // ERROR: https://github.com/webpack/webpack/issues/7378 // HACK FIX: https://github.com/TypeStrong/ts-loader/issues/653 class IgnoreNotFoundExportPlugin { - apply(compiler) { + apply(compiler: webpack.Compiler) { const messageRegExp = /export '.*'( \(reexported as '.*'\))? was not found in/; - const doneHook = stats => { + const doneHook = (stats: any) => { stats.compilation.warnings = stats.compilation.warnings.filter( - warn => !(warn && messageRegExp.test(warn.message)), + (warn: any) => !(warn && messageRegExp.test(warn.message)), ); }; if (compiler.hooks) { compiler.hooks.done.tap('IgnoreNotFoundExportPlugin', doneHook); } else { + // @ts-expect-error - TODO: this api is non existent thus this probably doesn't work anymore ? compiler.plugin('done', doneHook); } } @@ -60,23 +61,25 @@ const makeConfig = (srcPath: string, name: string): webpack.Configuration => ({ react: 'react', 'react-dom': 'reactDOM', }, - ...(argv.debug && { - optimization: { - minimizer: [ - new TerserWebpackPlugin({ - parallel: true, - terserOptions: { - mangle: false, - output: { - beautify: true, - comments: true, - preserve_annotations: true, - }, - }, - }), - ], - }, - }), + ...(argv.debug + ? { + optimization: { + minimizer: [ + new TerserWebpackPlugin({ + parallel: true, + terserOptions: { + mangle: false, + output: { + beautify: true, + comments: true, + preserve_annotations: true, + }, + }, + }), + ], + }, + } + : null), plugins: [ new CleanWebpackPlugin(), new IgnoreNotFoundExportPlugin(), @@ -94,7 +97,7 @@ const makeConfig = (srcPath: string, name: string): webpack.Configuration => ({ }, }); -const toEsDistPath = srcPath => { +const toEsDistPath = (srcPath: string) => { return paths.packageDist('react-northstar', path.join('es', srcPath)); }; diff --git a/scripts/webpack/webpack.config.ts b/scripts/webpack/webpack.config.ts index ed1f0603226f89..01634fa9c2b711 100644 --- a/scripts/webpack/webpack.config.ts +++ b/scripts/webpack/webpack.config.ts @@ -13,10 +13,11 @@ import getDefaultEnvironmentVars from './getDefaultEnvironmentVars'; const { paths } = config; const { __DEV__, __PERF__, __PROD__ } = config.compiler_globals; -const webpackConfig: webpack.Configuration = { +const webpackConfig: webpack.Configuration & + Required> & { entry: Record } = { name: 'client', target: 'web', - mode: config.compiler_mode, + mode: config.compiler_mode!, entry: { app: paths.docsSrc('index'), }, @@ -109,16 +110,17 @@ const webpackConfig: webpack.Configuration = { }), new webpack.ContextReplacementPlugin(/moment[/\\]locale$/, /en/), __DEV__ && - // Disable ProgressPlugin in CI and multi-project build because the outdated lines can't be deleted and - // spam the log (note that build:docs is the resolved command used by lerna run build --stream) - !process.env.TF_BUILD && - !process.argv.includes('build:docs') && - new webpack.ProgressPlugin({ - entries: true, - modules: true, - modulesCount: 500, - } as any), - ].filter(Boolean), + // Disable ProgressPlugin in CI and multi-project build because the outdated lines can't be deleted and + // spam the log (note that build:docs is the resolved command used by lerna run build --stream) + !process.env.TF_BUILD && + !process.argv.includes('build:docs') + ? new webpack.ProgressPlugin({ + entries: true, + modules: true, + modulesCount: 500, + }) + : null, + ].filter(Boolean) as webpack.WebpackPluginInstance[], resolve: { extensions: ['.ts', '.tsx', '.js', '.json'], alias: { @@ -165,8 +167,9 @@ if (__DEV__) { }, (val, key) => `&${key}=${val}`, ).join('')}`; - const entry = webpackConfig.entry as webpack.EntryObject; - entry.app = [webpackHotMiddlewareEntry].concat(entry.app as string); + const entry = webpackConfig.entry; + + entry.app = [webpackHotMiddlewareEntry].concat(entry.app); webpackConfig.plugins.push(new webpack.HotModuleReplacementPlugin(), new webpack.NoEmitOnErrorsPlugin()); } diff --git a/typings/find-free-port/index.d.ts b/typings/find-free-port/index.d.ts new file mode 100644 index 00000000000000..85bdaa08eb2c06 --- /dev/null +++ b/typings/find-free-port/index.d.ts @@ -0,0 +1,8 @@ +// Type definitions for find-free-port 2.0.0 + +declare module 'find-free-port' { + function findFreePort(args: [...values: Array, cb: (err: unknown, freePort: number) => void]): any; + function findFreePort(...args: Array): Promise; + + export default findFreePort; +} diff --git a/typings/find-free-port/tsconfig.json b/typings/find-free-port/tsconfig.json new file mode 100644 index 00000000000000..c6f8b0a935c4c2 --- /dev/null +++ b/typings/find-free-port/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": {}, + "include": ["*.d.ts"] +} diff --git a/typings/tsconfig.json b/typings/tsconfig.json index 7187a8f9e2b2ac..d2324f1f1c570d 100644 --- a/typings/tsconfig.json +++ b/typings/tsconfig.json @@ -27,6 +27,9 @@ }, { "path": "./json-stable-stringify-without-jsonify/tsconfig.json" + }, + { + "path": "./find-free-port/tsconfig.json" } ] } diff --git a/yarn.lock b/yarn.lock index 0ffd9c3a0d8e15..48a448c922b811 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5308,6 +5308,13 @@ dependencies: "@types/babel__core" "*" +"@types/babel__register@7.17.0": + version "7.17.0" + resolved "https://registry.yarnpkg.com/@types/babel__register/-/babel__register-7.17.0.tgz#fcb3ee6bfe9314215a0af5de7d9f19faf4144e50" + integrity sha512-/iL2G2eq5drPFQpzb/wMYk7B0LMWRpWajnPn1XuIzeQ2w+CRrzNbwJ5xpBdZkciYx0jqZa8h40dTXpMeTcGV7A== + dependencies: + "@types/babel__core" "*" + "@types/babel__template@*": version "7.0.2" resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.0.2.tgz#4ff63d6b52eddac1de7b975a5223ed32ecea9307" @@ -5713,6 +5720,13 @@ dependencies: "@types/node" "*" +"@types/graphviz@0.0.34": + version "0.0.34" + resolved "https://registry.yarnpkg.com/@types/graphviz/-/graphviz-0.0.34.tgz#40657ce27383e3bd36bea6a4b7449866f3caa8eb" + integrity sha512-5pyobgT+/NhwKy/LMLw14xFInvYXBPx4ITc2a5FvZbm6hcudcP73DpTKTlaZbjr8fdNAkaK9KdP8GAEF0iBwlQ== + dependencies: + "@types/node" "*" + "@types/gulp-babel@6.1.30": version "6.1.30" resolved "https://registry.yarnpkg.com/@types/gulp-babel/-/gulp-babel-6.1.30.tgz#72220a2e72517bd2cf461fc6250849035ad58798" @@ -6445,6 +6459,15 @@ "@types/expect" "^1.20.4" "@types/node" "*" +"@types/webpack-bundle-analyzer@4.4.3": + version "4.4.3" + resolved "https://registry.yarnpkg.com/@types/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.4.3.tgz#1f8eba759677a3bf094166572fc7debf68161f61" + integrity sha512-7+4XhCMxc1XyQr2lwAbEEeWTax+FX70xMeskU0WtxrODczoOZXTo9vrYh/XF7pAOn+NxqR4yRKW5gfz1HHNnfw== + dependencies: + "@types/node" "*" + tapable "^2.2.0" + webpack "^5" + "@types/webpack-dev-middleware@4.1.0": version "4.1.0" resolved "https://registry.yarnpkg.com/@types/webpack-dev-middleware/-/webpack-dev-middleware-4.1.0.tgz#3bf432f2ff2313b6470af21b748dd5d73dba5ec6" @@ -11909,10 +11932,10 @@ dotenv@~10.0.0: resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81" integrity sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q== -dotparser@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/dotparser/-/dotparser-1.0.0.tgz#32458fcfa855046705b90df929ed87a849e32304" - integrity sha512-yIvKnnQbK8dhh6aN44xNPd00FuHWVygAW8iYW05DCgzGzHJfe/VaQQAoq3QBDAa8cMFS7Lub8SCAsej/lLUomQ== +dotparser@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/dotparser/-/dotparser-1.1.1.tgz#c474942f329638b44ddb3dff27e9a6385dbab2f9" + integrity sha512-8ojhUts0HbLnXJgjTiJOddwVVBUk6hg4SJ5kGiuhzgK/f+y79TiWvICwx1oCWlVbBC8YI3nEaIQg9fjGYbGBXw== download@^7.1.0: version "7.1.0" @@ -13762,10 +13785,10 @@ find-cache-dir@^3.3.1: make-dir "^3.0.2" pkg-dir "^4.1.0" -find-free-port@~2.0.0: +find-free-port@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/find-free-port/-/find-free-port-2.0.0.tgz#4b22e5f6579eb1a38c41ac6bcb3efed1b6da9b1b" - integrity sha1-SyLl9leesaOMQaxryz7+0bbamxs= + integrity sha512-J1j8gfEVf5FN4PR5w5wrZZ7NYs2IvqsHcd03cAeQx3Ec/mo+lKceaVNhpsRKoZpZKbId88o8qh+dwUwzBV6WCg== find-replace@^3.0.0: version "3.0.0" @@ -14975,7 +14998,7 @@ graceful-fs@^4.2.9: resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" integrity sha1-TK+tdrxi8C+gObL5Tpo906ORpyU= -graphviz@0.0.9, graphviz@^0.0.9: +graphviz@0.0.9: version "0.0.9" resolved "https://registry.yarnpkg.com/graphviz/-/graphviz-0.0.9.tgz#0bbf1df588c6a92259282da35323622528c4bbc4" integrity sha512-SmoY2pOtcikmMCqCSy2NO1YsRfu9OO0wpTlOYW++giGjfX1a6gax/m1Fo8IdUd0/3H15cTOfR1SMKwohj4LKsg== @@ -26355,7 +26378,7 @@ terser@5.14.2, terser@^5.3.4, terser@^5.5.1, terser@^5.7.2: source-map-support "~0.5.20" terser@^4.1.2, terser@^4.3.9, terser@^4.6.12, terser@^4.6.2, terser@^4.6.3: - version "4.8.1" + version "4.8.0" resolved "https://registry.yarnpkg.com/terser/-/terser-4.8.0.tgz#63056343d7c70bb29f3af665865a46fe03a0df17" integrity sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw== dependencies: