We have refactored our project to be a mono repository (NPM Workspaces) and structure it like so:
|-- apps
|-- native <-- Not Workspace
|-- web <-- Not Workspace
|-- common
|-- models <-- Workspace
|-- connectors <-- Workspace
|-- store <-- Workspace
|-- types <-- Workspace
|-- utils <-- Workspace
-- package-lock.json
-- package.json
Our native
and web
apps use code from common
and do not share code between them.
root package.json
-----------------
{
"name": "@secret/client",
"version": "1.0.0",
"private": true,
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"workspaces": [
"./common/*"
]
}
When compiling Typescript files, only the Typescript files in the common
folder fail to compile and throw Parsing error: Unexpected token
errors. The rest of Typescript files in native
and web
compile correctly.
Webpack module.rules
[
{ parser: { requireEnsure: false } },
{
oneOf: [
...irrelevantRulesIntentionallyHidden,
{
test: /\.(js|mjs|jsx|ts|tsx)$/,
include: 'C:\\_git\\Secret\\src\\Secret.WebClient\\ClientApp',
loader: 'C:\\_git\\Secret\\src\\Secret.WebClient\\ClientApp\\apps\\web\\node_modules\\babel-loader\\lib\\index.js',
options: {
customize: 'C:\\_git\\Secret\\src\\Secret.WebClient\\ClientApp\\apps\\web\\node_modules\\babel-preset-react-app\\webpack-overrides.js',
presets: [
[
'C:\\_git\\Secret\\src\\Secret.WebClient\\ClientApp\\apps\\web\\node_modules\\babel-preset-react-app\\index.js',
[Object]
]
],
babelrc: false,
configFile: false,
cacheIdentifier: 'development:[email protected]:[email protected]:[email protected]:[email protected]',
plugins: [
[
'C:\\_git\\Secret\\src\\Secret.WebClient\\ClientApp\\apps\\web\\node_modules\\babel-plugin-named-asset-import\\index.js',
[Object]
],
'C:\\_git\\Secret\\src\\Secret.WebClient\\ClientApp\\apps\\web\\node_modules\\react-refresh\\babel.js'
],
cacheDirectory: true,
cacheCompression: false,
compact: false,
sourceType: 'unambiguous'
}
},
{
test: /\.(js|mjs)$/,
exclude: /@babel(?:\/|\\{1,2})runtime/,
loader: 'C:\\_git\\Secret\\src\\Secret.WebClient\\ClientApp\\apps\\web\\node_modules\\babel-loader\\lib\\index.js',
options: {
babelrc: false,
configFile: false,
compact: false,
presets: [
[
'C:\\_git\\Secret\\src\\Secret.WebClient\\ClientApp\\apps\\web\\node_modules\\babel-preset-react-app\\dependencies.js',
[Object]
]
],
cacheDirectory: true,
cacheCompression: false,
cacheIdentifier: 'development:[email protected]:[email protected]:[email protected]:[email protected]',
sourceMaps: true,
inputSourceMap: true,
sourceType: 'unambiguous'
},
include: 'C:\\_git\\Secret\\src\\Secret.WebClient\\ClientApp'
},
]
}
]
ESLintWebpackPlugin
{
key: 'ESLintWebpackPlugin',
options: {
extensions: [ 'js', 'mjs', 'jsx', 'ts', 'tsx' ],
emitError: true,
emitWarning: true,
failOnError: true,
formatter: 'C:\\_git\\Secret\\src\\Secret.WebClient\\ClientApp\\apps\\web\\node_modules\\react-scripts\\node_modules\\react-dev-utils\\eslintFormatter.js',
eslintPath: 'C:\\_git\\Secret\\src\\Secret.WebClient\\ClientApp\\apps\\web\\node_modules\\eslint\\lib\\api.js',
context: 'C:\\_git\\Secret\\src\\Secret.WebClient\\ClientApp',
cache: true,
cacheLocation: 'C:\\_git\\Secret\\src\\Secret.WebClient\\ClientApp\\apps\\web\\node_modules\\.cache\\.eslintcache',
cwd: 'C:\\_git\\Secret\\src\\Secret.WebClient\\ClientApp\\apps\\web',
resolvePluginsRelativeTo: 'C:\\_git\\Secret\\src\\Secret.WebClient\\ClientApp\\apps\\web\\node_modules\\react-scripts\\config',
baseConfig: {
extends: [
'C:\\_git\\Secret\\src\\Secret.WebClient\\ClientApp\\apps\\web\\node_modules\\eslint-config-react-app\\base.js'
],
rules: {}
},
overrideConfig: {
rules: {
'@typescript-eslint/no-unused-vars': [ 0 ],
'no-use-before-define': [ 0 ],
'no-useless-escape': [ 0 ],
'jsx-a11y/anchor-is-valid': [ 0 ],
'unicode-bom': [ 0 ],
'react/button-has-type': [ 2, [Object] ],
'react/jsx-no-literals': [ 2, [Object] ]
},
plugins: [ 'react' ]
},
ignore: true
},
run: [Function: bound run] AsyncFunction
}
ForkTsCheckerWebpackPlugin
{
eslint: false,
eslintOptions: {},
tsconfigPath: undefined,
compiler: undefined,
started: undefined,
elapsed: undefined,
cancellationToken: undefined,
isWatching: false,
checkDone: false,
compilationDone: false,
diagnostics: [],
lints: [],
eslintVersion: undefined,
startAt: 0,
nodeArgs: [],
options: {
typescript: 'C:\\_git\\Secret\\src\\Secret.WebClient\\ClientApp\\apps\\web\\node_modules\\typescript\\lib\\typescript.js',
async: true,
checkSyntacticErrors: true,
resolveModuleNameModule: undefined,
resolveTypeReferenceDirectiveModule: undefined,
tsconfig: 'C:\\_git\\Secret\\src\\Secret.WebClient\\ClientApp\\apps\\web\\tsconfig.json',
reportFiles: [Array],
silent: true,
formatter: undefined
},
ignoreDiagnostics: [],
ignoreLints: [],
ignoreLintWarnings: false,
reportFiles: [
'../**/src/**/*.{ts,tsx}',
'**/src/**/*.{ts,tsx}',
'!**/src/**/__tests__/**',
'!**/src/**/?(*.)(spec|test).*',
'!**/src/setupProxy.*',
'!**/src/setupTests.*'
],
logger: Object [console] {...}, <--- too long to paste
typescriptPath: 'C:\\_git\\Secret\\src\\Secret.WebClient\\ClientApp\\apps\\web\\node_modules\\typescript\\lib\\typescript.js',
typescriptVersion: '4.2.3',
tsconfig: 'C:\\_git\\Secret\\src\\Secret.WebClient\\ClientApp\\apps\\web\\tsconfig.json',
compilerOptions: {},
vue: { compiler: 'vue-template-compiler', enabled: false },
useTypescriptIncrementalApi: true,
measureTime: false
}
Example: common/connectors/package.json
{
"name": "@secret/connectors",
"version": "1.0.0",
"private": true,
"description": "",
"main": "index.js",
"dependencies": {
"@types/lodash": "^4.14.165",
"@types/react": "^17.0.3",
"lodash": "^4.17.20",
"react-redux": "^7.2.3",
"typescript": "^4.2.3"
},
"devDependencies": {
"eslint-plugin-react": "^7.23.1",
"tslint": "^5.20.1",
"tslint-react": "^4.1.0"
}
}
There is a bug in ForkTsCheckerWebpackPlugin create-react-app (CRA) uses. Updating it to the latest version (at the time of writing 6.2.10
) and using this CRA override solves the issue:
// Use newer version of ForkTSCheckerWebpackPlugin to type check
// files across the monorepo.
const forkTsCheckerWebpackPlugin = config.plugins.findIndex(
(p) => p.reportFiles
);
if (forkTsCheckerWebpackPlugin !== -1) {
config.plugins.splice(
forkTsCheckerWebpackPlugin,
1,
new ForkTSCheckerWebpackPlugin({
issue: {
// The exclude rules are copied from CRA.
exclude: [
{
file: "**/src/**/__tests__/**",
},
{
file: "**/src/**/?(*.)(spec|test).*",
},
{
file: "**/src/setupProxy.*",
},
{
file: "**/src/setupTests.*",
},
],
},
})
);
}
Solution courtesy: @NiGhTTraX https://github.com/NiGhTTraX/ts-monorepo/issues/74