diff --git a/packages/dashboard/src/initialization/plugins.d.ts b/packages/dashboard/src/initialization/plugins.d.ts new file mode 100644 index 000000000000..0a5c2294e09a --- /dev/null +++ b/packages/dashboard/src/initialization/plugins.d.ts @@ -0,0 +1,2 @@ +// This file is used to skip the type check to the ./plugins file. +export {} diff --git a/packages/dashboard/src/initialization/plugins.ts b/packages/dashboard/src/initialization/plugins.js similarity index 100% rename from packages/dashboard/src/initialization/plugins.ts rename to packages/dashboard/src/initialization/plugins.js diff --git a/packages/dashboard/tsconfig.json b/packages/dashboard/tsconfig.json index 06936d8e8719..a2190a233c01 100644 --- a/packages/dashboard/tsconfig.json +++ b/packages/dashboard/tsconfig.json @@ -6,6 +6,7 @@ "tsBuildInfoFile": "./dist/src.tsbuildinfo" }, "include": ["./src", "./src/**/*.json"], + "exclude": ["./src/initialization/plugins.js"], "references": [ { "path": "../public-api" }, { "path": "../theme/" }, diff --git a/packages/mask/src/plugin-infra/register.d.ts b/packages/mask/src/plugin-infra/register.d.ts new file mode 100644 index 000000000000..43a78b9c1ee9 --- /dev/null +++ b/packages/mask/src/plugin-infra/register.d.ts @@ -0,0 +1,2 @@ +// This file is used to skip the type check to the ./register file. +export {} diff --git a/packages/mask/src/plugin-infra/register.ts b/packages/mask/src/plugin-infra/register.js similarity index 90% rename from packages/mask/src/plugin-infra/register.ts rename to packages/mask/src/plugin-infra/register.js index c1347d39ff8c..e49387466b4e 100644 --- a/packages/mask/src/plugin-infra/register.ts +++ b/packages/mask/src/plugin-infra/register.js @@ -1,3 +1,4 @@ +// This file is a JavaScript file because it's reference to the plugins should not be counted as a project reference. // If your plugin also works in isolated dashboard, please also register it in // packages/dashboard/src/initialization/plugins.ts diff --git a/packages/mask/src/tsconfig.json b/packages/mask/src/tsconfig.json index 7bdabfffe9d5..b038873061a0 100644 --- a/packages/mask/src/tsconfig.json +++ b/packages/mask/src/tsconfig.json @@ -8,6 +8,7 @@ "lib": ["ES2021"] }, "include": ["./", "./**/*.json"], + "exclude": ["./plugin-infra/register.js"], // ! Note: mask/ depends dashboard/ on source-code level // ! but dashboard/ depends mask/ on type level diff --git a/packages/scripts/src/commands/new-package.ts b/packages/scripts/src/commands/new-package.ts index 4e52a98be061..08834b34079c 100644 --- a/packages/scripts/src/commands/new-package.ts +++ b/packages/scripts/src/commands/new-package.ts @@ -115,13 +115,16 @@ async function createNewPackage({ path, npmName, type, pluginID }: PackageOption content.replace(INSERT_HERE, `${NormativeName} = '${pluginID}'\n${INSERT_HERE}`), ) await changeFile.typescript( - resolve(ROOT_PATH, `packages/mask/src/plugin-infra/register.ts`), + resolve(ROOT_PATH, `packages/mask/src/plugin-infra/register.js`), (content) => `${content}import '${npmName}'`, ) await awaitChildProcess(shell.cwd(ROOT_PATH)`pnpm install --prefer-offline -C packages/mask ${npmName}`) await changeFile(resolve(ROOT_PATH, 'packages/mask/package.json'), (content) => content.replaceAll(/workspace:\^undefined/g, 'workspace:*'), ) + await changeFile(resolve(ROOT_PATH, 'tsconfig.json'), (content) => + content.replace(INSERT_HERE + ' 3', `"${npmName}": ["./${path}/src"],\n ${INSERT_HERE} 3`), + ) } else { // cp -r packages/empty packages/NEW_PACKAGE await copy(resolve(ROOT_PATH, 'packages/empty'), packagePath, { @@ -134,9 +137,11 @@ async function createNewPackage({ path, npmName, type, pluginID }: PackageOption content.name = npmName }) await changeFile(resolve(ROOT_PATH, 'tsconfig.json'), (content) => - content.replace(INSERT_HERE, `${INSERT_HERE}\n { "path": "./${path}/tsconfig.tests.json" },`), + content + .replace(INSERT_HERE + ' 1', `${INSERT_HERE} 1\n { "path": "./${path}/tsconfig.tests.json" },`) + .replace(INSERT_HERE + ' 2', `"${npmName}": ["./${path}/src"],\n ${INSERT_HERE} 2`), ) - await changeFile(resolve(packagePath, 'README.md'), (content) => `# ${npmName}`) + await changeFile(resolve(packagePath, 'README.md'), () => `# ${npmName}`) } // regenerate lockfile and install dependencies for newly installed packages diff --git a/tsconfig.json b/tsconfig.json index f57ae37e680d..35a2de37afa5 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -12,7 +12,7 @@ { "path": "./packages/dashboard/stories" }, { "path": "./packages/theme/stories" }, // Tests - // @masknet/scripts: insert-here + // @masknet/scripts: insert-here 1 { "path": "./packages/backup-format/tsconfig.tests.json" }, { "path": "./packages/encryption/tsconfig.tests.json" }, { "path": "./packages/shared-base/tsconfig.tests.json" }, @@ -20,6 +20,85 @@ { "path": "./packages/plugins" } ], "compilerOptions": { + "paths": { + // # Why? + // TypeScript project reference requires manually declaring all referenced projects. + + // For example, we have 2 projects A and B under /projects/a and /projects/b and B depends on A, + // if we do not list A in the references, when we write + + // > /packages/b/src/index.ts + + // ```ts + // import { something } from '../../a' + // ``` + + // If there is no "reference": [{ "path": "../a/tsconfig.json" }] in /packages/b/tsconfig.json, + // TypeScript will complain TS6307: "File '/packages/b/src/index.ts' is not listed within the file list of the project '/packages/b/tsconfig.json'. Projects must list all files or use an 'include' pattern." + + // This is error is GOOD because it means TypeScript _can_ automatically detect the missing references. + + // # What about monorepo? + // But TypeScript cannot detect the missing references when we install them as monorepo packages. + // Still the project structure above, but A is installed as "@masknet/a" in /packages/b/node_modules/@masknet/a, + // TypeScript will accept the missing reference to the A project, + // because it will go through the "moduleResolution": "Node" resolution, if `.d.ts` file is found, + // the project will _accidentally_ compiles, but TypeScript does DON'T know those packages has dependencies in the project reference graph. + // This will cause the project to randomly fail to be type-checked. + + // # Solution + // We use "paths" to map the Node-style import path back to the relative style, + // therefore TypeScript can check the missing dependencies again. + + // This will not introduce problems like mapping "@src/" to "/packages/a/src" which is problematic, + // because those projects (if they're correctly configured) is installed in the node_modules, + // so if any toolchain does not support the "paths" feature, it will still work. + + // This will bring another problem: now we can reference a project without installing it as a monorepo dependency, + // which is unwanted. We need to take care of this. + + "@masknet/backup-format": ["./packages/backup-format/src"], + // ! dashboard is explicitly opt-out of this feature, it and the /packages/mask has circulare reference that TypeScript does not allows. + // "@masknet/dashboard": ["./packages/dashboard/src"], + "@masknet/encryption": ["./packages/encryption/src"], + "@masknet/external-plugin-previewer": ["./packages/external-plugin-previewer/src"], + "@masknet/gun-utils": ["./packages/gun-utils/src"], + "@masknet/icons": ["./packages/icons"], + "@masknet/injected-script": ["./packages/injected-script/sdk"], + // @masknet/mask is not listed. it does not have a exports/main field in the package.json. + "@masknet/mask-sdk": ["./packages/mask-sdk/server"], + "@masknet/plugin-infra": ["./packages/plugin-infra/src"], + "@masknet/global-types/*": ["./packages/polyfills/types/*"], + "@masknet/provider-proxy": ["./packages/provider-proxy/src"], + "@masknet/public-api": ["./packages/public-api/src"], + // @masknet/scripts is not listed. It does not mean to be installed as a dependency and not join the type check. + "@masknet/shared": ["./packages/shared/src"], + "@masknet/shared-base": ["./packages/shared-base/src"], + "@masknet/storybook-shared": ["./packages/storybook-shared/src"], + "@masknet/theme": ["./packages/theme/src"], + // @masknet/web3-constants is not listed. It is not a TS project (only contains JSON files). + // @masknet/web3-contracts is not listed. It is not a TS project (only contains generated JSON and .d.ts files). + "@masknet/web3-kit": ["./packages/web3-kit/src"], + "@masknet/web3-providers": ["./packages/web3-providers/src"], + "@masknet/web3-shared-base": ["./packages/web3-shared/base/src"], + "@masknet/web3-shared-evm": ["./packages/web3-shared/evm/src"], + "@masknet/web3-shared-flow": ["./packages/web3-shared/flow/src"], + "@masknet/web3-shared-solana": ["./packages/web3-shared/solana/src"], + // @masknet/scripts: insert-here 2 + + // Plugins + "@masknet/plugin-dao": ["./packages/plugins/DAO/src"], + "@masknet/plugin-debugger": ["./packages/plugins/Debugger/src"], + "@masknet/plugin-example": ["./packages/plugins/example/src"], + "@masknet/plugin-file-service": ["./packages/plugins/FileService/src"], + "@masknet/plugin-flow": ["./packages/plugins/Flow/src"], + "@masknet/plugin-rss3": ["./packages/plugins/RSS3/src"], + "@masknet/plugin-solana": ["./packages/plugins/Solana/src"], + "@masknet/plugin-template": ["./packages/plugins/template/src"], + "@masknet/plugin-cyber-connect": ["./packages/plugins/CyberConnect/src"], + // @masknet/scripts: insert-here 3 + "@masknet/plugin-wallet": ["./packages/plugins/Wallet/src"] + }, // Classification follows https://www.typescriptlang.org/tsconfig // Type Checking