Skip to content

Express and Webpack with Typescript

Directory

--rets
 |--api
 |--app

api

package.json

{
  "name": "api",
  "version": "1.0.0",
  "description": "",
  "main": "server.js",
  "type": "module",
  "scripts": {
    "dev": "nodemon"
  },
  "dependencies": {
    "express": "^4.17.1",
    "http-proxy-middleware": "^1.0.4"
  },
  "devDependencies": {
    "@types/express": "^4.17.11",
    "@types/node": "^15.3.1",
    "nodemon": "^2.0.7",
    "ts-node": "^9.1.1",
    "typescript": "^4.2.4"
  },
  "author": "",
  "license": "ISC"
}

nodemon.json

{
  "verbose":true,
  "watch": ["server.ts"],
  "ext": "ts,json",
  "ignore": ["node_modules"],
  "exec": "node --loader ts-node/esm server.ts"
}

npx tsc -init to get initial tsconfig.json

tsconfig.json

{
  "compilerOptions": {
    /* Visit https://aka.ms/tsconfig.json to read more about this file */

    /* Basic Options */
    "incremental": true,                         /* Enable incremental compilation */
    "target": "es6",                                /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
    "module": "es2020",                           /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
    "strict": true,                                 /* Enable all strict type-checking options. */
    "esModuleInterop": true,                        /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
    "skipLibCheck": true,                           /* Skip type checking of declaration files. */
    "forceConsistentCasingInFileNames": true        /* Disallow inconsistently-cased references to the same file. */
  }
}

server.ts

import express from 'express';
const port = 3080

const app = express();

app.get('/', (req, res) => {
    res.send('Well done!');
})

app.listen(port, () => {
    console.log('The application is listening on port '+port);
})

app

The npm module react offer a program create-react-app that is meant to called from outside the target directory app. If react is not installed globally, it is a chicken and egg problem. It is solved here by temporarily installing react in the parent dir rets, calling create-react-app, then deleteing top's node_modules and package.json in rets.

The react app can serve itself. However, we do it via express. In order for that to work, a line must be added to the template package.json:

  "proxy": "http://localhost:3080",

Ran npm run eject and the resulting environment is too complex. Changing to app2

app2

package.json

{
  "name": "app2",
  "version": "1.0.0",
  "main": "src/index.ts",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "serve": "webpack serve --config ./webpack.config.dev.js --progress",    
    "build": "webpack --config ./webpack.config.prod.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "description": "",
  "devDependencies": {
    "@types/lodash": "^4.14.170",
    "ts-loader": "^9.2.1",
    "typescript": "^4.2.4",
    "webpack": "^5.37.1",
    "webpack-cli": "^4.7.0",
    "webpack-dev-server": "^3.11.2"
  },
  "dependencies": {
    "lodash": "^4.17.21"
  }
}

tsconfig.json

{
  "compilerOptions": {
    "outDir": "./dist/",
    "sourceMap": true,
    "noImplicitAny": true,
    "module": "es6",
    "target": "es5",
    "jsx": "react",
    "allowJs": true,
    "moduleResolution": "node",
  }
}

webpack.config.dev.js

const path = require('path');
const dist_dir = 'dist-dev'
module.exports = {
  mode: 'development',
  entry: './src/index.ts',
  devServer: {
    contentBase: path.join(__dirname, 'dist-dev'),
    watchContentBase: true,
    proxy: {
      '/api': 'http://localhost:3080',
    },        
  },  
  devtool: 'inline-source-map',
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        use: 'ts-loader',
        exclude: /node_modules/,
      },
    ],
  },
  resolve: {
    extensions: ['.tsx', '.ts', '.js'],
  },
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, dist_dir),
  },
};

webpack.config.dev.js

NOT YET TESTED

src/index.ts

import * as _ from 'lodash';

function component(s:string) {
  const element = document.createElement('div');

  element.innerHTML = s; //_.join(['Hello', 'foopack'], ' ');

  return element;
}

async function main(){
  document.body.appendChild(component(_.join(['Hello', 'foopack'],' ')));
  const data = await fetch('/api')
  .then(response => response.text())
  //.then(data => console.log(data));
  console.log(data)
  document.body.appendChild(component(data));
}
main()

dest-dev/index.html (hard coded for now)

<html>
  <head>
    <meta charset="utf-8" />
  </head>
  <body>
    <script
      type="text/javascript"
      charset="utf-8"
      src="/bundle.js"
    ></script>
    <div>1</div>
    <div>2</div>
    <div>3</div>
  </body>
</html>