-
Notifications
You must be signed in to change notification settings - Fork 74
v1 TypeScript
starter-kit-ts
isn't actively maintained, but shows how the starter kit can run with full-stack TypeScript. The steps, as of last time I needed to do this, are:
-
Remove Babel and the Webpack loader:
$ npm uninstall @babel/{cli,core,eslint-parser,node,plugin-proposal-class-properties,plugin-transform-runtime,preset-env,preset-react,runtime} babel-loader
and delete all of the
.babelrc
files. -
Install TypeScript with the relevant types and helpers:
$ npm install --save-dev typescript ts-{jest,loader,node} @types/{jest,morgan,node,react,react-dom,supertest} @typescript-eslint/{eslint-plugin,parser}
-
Update the scripts in
package.json
:"prebuild": "npm run cleanup", "build": "npm run build:server && npm run build:client", "postbuild": "./bin/build-info.sh || echo 'No build info'", - "build:client": "webpack --config client/webpack/prod.config.js", - "build:server": "babel server --out-dir dist --ignore \"**/*.test.js,server/static\"", + "build:client": "webpack --config client/webpack/prod.config.ts", + "build:server": "tsc -p ./server", "cleanup": "rimraf ./dist/*", "dev": "concurrently -k -n \"client,server\" \"npm run dev:client\" \"npm run dev:server\"", - "dev:client": "webpack serve --config client/webpack/dev.config.js", - "dev:server": "cross-env PORT=3100 nodemon --exitcrash --inspect --delay 500ms --watch server --exec babel-node server/server.js", + "dev:client": "webpack serve --config client/webpack/dev.config.ts", + "dev:server": "cross-env PORT=3100 nodemon --exitcrash --inspect --delay 500ms --watch server --exec node --require ts-node/register server/server.ts", "e2e": "concurrently -k -s first -n \"app,e2e\" \"npm run serve\" \"npm run e2e:safe\"", "e2e:dev": "concurrently -k -s first -n \"dev,e2e\" \"npm run dev\" \"npm run e2e:safe\"", "e2e:local": "cypress open", "e2e:run": "cypress run", "e2e:safe": "wait-on -l http-get://localhost:3000 && npm run e2e:run", - "lint": "eslint .", + "lint": "eslint . --ext=.js,.jsx,.ts,.tsx", "preserve": "npm run build", "serve": "npm start", "ship": "npm run lint && npm run test && npm run e2e:dev && npm run e2e",
-
Update the ESLint configuration in
.eslintrc.json
:"extends": [ - "@codeyourfuture/standard" + "@codeyourfuture/standard", + "plugin:@typescript-eslint/eslint-recommended", + "plugin:@typescript-eslint/recommended" ],
- "parser": "@babel/eslint-parser" + "parser": "@typescript-eslint/parser", + "parserOptions": { + "sourceType": "module" + }, + "plugins": ["@typescript-eslint"]
-
Update the Jest configuration:
-module.exports = { +const typescript = { globals: { "ts-jest": { tsconfig: "tsconfig.test.json", }, }, preset: "ts-jest", +}; + +module.exports = { projects: [ { + ...typescript, displayName: "client", moduleNameMapper: { - "\\.(png|svg|jpe?g|gif|css)$": "<rootDir>/__mocks__/fileMock.ts", + "\\.(png|svg|jpe?g|gif|css)$": "<rootDir>/__mocks__/fileMock.js", }, setupFilesAfterEnv: [ "<rootDir>/client/setupTests.ts", ], testEnvironment: "jsdom", testMatch: [ - "<rootDir>/client/**/*.test.ts", + "<rootDir>/client/**/*.test.ts(x)", ], }, { + ...typescript, displayName: "server", testEnvironment: "node", testMatch: [
-
Update the Cypress configuration:
-
e2e/plugins/index.js
(note this remains JavaScript):-// eslint-disable-next-line no-unused-vars +// eslint-disable-next-line @typescript-eslint/no-unused-vars
-
-
Rename and update the Webpack configuration:
-
client/webpack/common.config.ts
:-const HtmlWebpackPlugin = require("html-webpack-plugin"); +import HtmlWebpackPlugin from "html-webpack-plugin"; +import { Configuration } from "webpack"; -module.exports = { - entry: "./client/src/index.js", +const configuration: Configuration = { + entry: "./client/src/index.tsx", module: { rules: [ { - test: /\.js$/, + test: /\.[jt]sx?$/, exclude: /node_modules/, - use: { - loader: "babel-loader", - options: { - cacheDirectory: true, - }, - }, + loader: "ts-loader", }, { test: /\.(png|svg|jpe?g|gif)$/,
template: "./client/src/index.html", }), ], + resolve: { + extensions: [".js", ".jsx", ".ts", ".tsx"], + }, }; + +export default configuration;
-
client/webpack/dev.config.ts
:-const { merge } = require("webpack-merge"); +import { merge } from "webpack-merge"; +import "webpack-dev-server"; -const common = require("./common.config"); +import common from "./common.config"; -module.exports = merge(common, { +const configuration = merge(common, { devtool: "inline-source-map", devServer: { historyApiFallback: true,
}, mode: "development", }); + +export default configuration;
-
client/webpack/prod.config.ts
:-const HtmlWebpackTagsPlugin = require("html-webpack-tags-plugin"); -const path = require("path"); -const { merge } = require("webpack-merge"); +import HtmlWebpackTagsPlugin from "html-webpack-tags-plugin"; +import path from "path"; +import { merge } from "webpack-merge"; -const common = require("./common.config"); -const { devDependencies } = require("../../package.json"); +import common from "./common.config"; +import { devDependencies } from "../../package.json"; -module.exports = merge(common, { +const configuration = merge(common, { devtool: "source-map", mode: "production", optimization: {
}, plugins: [ new HtmlWebpackTagsPlugin({ - scripts: [ + scripts: ([ { packageName: "react", variableName: "React" }, { packageName: "react-dom", variableName: "ReactDOM" }, - ].map(({ packageName, variableName }) => ({ + ] as const).map(({ packageName, variableName }) => ({ attributes: { crossorigin: "" }, external: { packageName, variableName }, path: `https://unpkg.com/${packageName}@${devDependencies[packageName]}/umd/${packageName}.production.min.js`,
}), ], }); + +export default configuration;
-
-
Update the Dockerfile:
- COPY ./.babelrc . + COPY ./tsconfig.json .
-
Add
client/src/assets.d.ts
to provide a definition for the import oflogo.svg
:declare module "*.svg" { const content: string; export default content; }
-
Add
tsconfig.json
files as follows:-
client/tsconfig.json
(used by Webpack):{ "compilerOptions": { "lib": [ "DOM" ] }, "exclude": [ "**/*.test.ts", "**/*.test.tsx" ], "extends": "../tsconfig.json", "include": [ "**/*.d.ts" ] }
-
e2e/tsconfig.json
(used by Cypress):{ "compilerOptions": { "types": [ "cypress", "@testing-library/cypress" ] }, "extends": "../tsconfig.json" }
-
server/tsconfig.json
(used bytsc
):{ "compilerOptions": { "lib": [ "ES2021" ], "outDir": "../dist", "target": "ES2021", "types": [ "node" ] }, "exclude": [ "**/*.test.ts", "**/*.test.tsx" ], "extends": "../tsconfig.json", "files": [ "./server.ts" ] }
Note: this assumes the default Node 16 runtime, per https://stackoverflow.com/a/67371788/3001761, see other versions of the question for other Node runtime versions.
-
tsconfig.test.json
(used by Jest):{ "compilerOptions": { "lib": [ "DOM", "ES2021" ], "target": "ES2021", "types": [ "jest", "testing-library__jest-dom" ] }, "extends": "./tsconfig.json" }
-
tsconfig.json
(used by all of the other configs, generated with./node_modules/.bin/tsc --init
):
-
-
Rename all remaining
.js
files to.ts
, except for:__mocks__/fileMock.js
- Anything in
bin/
e2e/plugins/index.js
server/static/main.js
-
Fix any remaining type errors/lint warnings