Search code examples
typescriptwebpackgoogle-chrome-extension

How to handle script tag inside html file in chrome extension in typescript with webpack


build a chrome extension in javascript which runs fine and now decided to turn into typescript so use webpack and tsconfig file and did necessary changes, extension loaded successfully but here are few issues.

I have changed extension of all .js file to .ts which reside in my scripts folder and these js file being used in html page for eg.popup.html

so how to handle this using webpack, my file being copied directly from scripts to dist.

folder structure is as following

|-src
│   background.ts
│
├───html
│       devtools.html
│       options.html
│       panel.html
│       popup.html
│
├───images
│       icon16.png
│       icon32.png
│
├───scripts
│       devtools.ts
│       options.ts
│       panel.ts
│       popup.ts
│
└───styles
│       base.css
│       dark.css
│       light.css
│       options.css
│       popup.css
│       style.css
│  
│
|-dist
│   background.js
│   manifest.json
│
├───html
│       devtools.html
│       options.html
│       panel.html
│       popup.html
│
├───images
│       icon16.png
│       icon32.png
│
├───scripts
│       devtools.ts
│       options.ts
│       panel.ts
│       popup.ts
│
├───styles
│       base.css
│       dark.css
│       light.css
│       options.css
│       popup.css
│       style.css
│
└───_locales
│    └───en
│           messages.json
└───public
│    └-manifest.json
└───webpack
    └-webpack.config.js

webpack.config.js

const path = require('path');
const CopyPlugin = require('copy-webpack-plugin');
module.exports = {
  mode: 'production',
  entry: {
    background: path.resolve(__dirname, '..', 'src', 'background.ts'),
  },
  output: {
    path: path.join(__dirname, '../dist'),
    filename: '[name].js',
  },
  resolve: {
    extensions: ['.ts', '.js'],
  },
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        loader: 'ts-loader',
        exclude: /node_modules/,
      },
    ],
  },
  plugins: [
    new CopyPlugin({
      patterns: [
        { from: '.', to: '.', context: 'public' },
        {
          from: path.resolve(__dirname, '..', 'src', 'scripts'),
          to: path.resolve(__dirname, '..', 'dist', 'scripts'),
          context: 'public',
        },
        {
          from: path.resolve(__dirname, '..', 'src', 'styles'),
          to: path.resolve(__dirname, '..', 'dist', 'styles'),
          context: 'public',
        },
        {
          from: path.resolve(__dirname, '..', 'src', 'html'),
          to: path.resolve(__dirname, '..', 'dist', 'html'),
          context: 'public',
        },
        {
          from: path.resolve(__dirname, '..', '_locales'),
          to: path.resolve(__dirname, '..', 'dist', '_locales'),
          toType: 'dir',
          context: 'public',
        },
        {
          from: path.resolve(__dirname, '..', 'src', 'images'),
          to: path.resolve(__dirname, '..', 'dist', 'images'),
          context: 'public',
        },
      ],
    }),
  ],
};

tsconfig.json

{
  "compilerOptions": {
    "strict": false,
    "module": "commonjs",
    "target": "es6",
    "esModuleInterop": true,
    "sourceMap": true,
    "allowJs": true,
    "outDir": "dist/js",
    "noEmitOnError": true,
    "typeRoots": ["node_modules/@types"]
  },
  "include": ["./src/**/*"]
}

src/html/popup.html


<!DOCTYPE html>
<html>

<head>
  <link type="text/css" rel="stylesheet" href="../styles/popup.css">
</head>

<body class="popup--container">
  <div class="popup--body">
    <h3>Tag Watcher</h3>
    <p>Open Dev Tools (<code>Cmd+Shift+I</code>) and click on <mark>Tagging</mark> tab to watch tagging details for the
      current page</p>
    <button class="btn--option" id="go-to-options" title="click to open options">Go to options</button>
  </div>
  <script async src="../scripts/popup.js"></script>
</body>

</html>

so here popup.js is no more a file as I have changed extension, so how do we fix this

package.json

{
  "dependencies": {},
  "devDependencies": {
    "@types/chrome": "0.0.203",
    "copy-webpack-plugin": "^11.0.0",
    "ts-loader": "^9.4.2",
    "typescript": "^4.9.4",
    "webpack": "^5.75.0",
    "webpack-cli": "^5.0.1"
  },
  "scripts": {
    "build": "webpack --config webpack/webpack.config.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  }
}

Solution

  • after many attempts, this config finally works for me

    and yes there are chances to improve this config. somoen can help here too.

    webpack.config.js

    const path = require('path');
    const CopyPlugin = require('copy-webpack-plugin');
    module.exports = {
      mode: 'production',
      entry: {
        background: path.resolve(__dirname, '..', 'src', 'background.ts'),
        'scripts/popup': path.resolve(__dirname, '..', 'src', 'scripts', 'popup.ts'),
        'scripts/options': path.resolve(__dirname, '..', 'src', 'scripts', 'options.ts'),
        'scripts/panel': path.resolve(__dirname, '..', 'src', 'scripts', 'panel.ts'),
        'scripts/devtools': path.resolve(__dirname, '..', 'src', 'scripts', 'devtools.ts'),
      },
      output: {
        path: path.join(__dirname, '..', 'dist'),
        filename: '[name].js',
        clean: true,
      },
      devtool: 'inline-source-map',
      resolve: {
        extensions: ['.ts', '.js'],
      },
      module: {
        rules: [
          {
            test: /\.tsx?$/,
            loader: 'ts-loader',
            exclude: /node_modules/,
          },
        ],
      },
      plugins: [
        new CopyPlugin({
          patterns: [
            { from: '.', to: '.', context: 'public' },
            {
              from: path.resolve(__dirname, '..', 'src', 'styles'),
              to: path.resolve(__dirname, '..', 'dist', 'styles'),
              context: 'public',
            },
            {
              from: path.resolve(__dirname, '..', 'src', 'html'),
              to: path.resolve(__dirname, '..', 'dist', 'html'),
              context: 'public',
            },
            {
              from: path.resolve(__dirname, '..', '_locales'),
              to: path.resolve(__dirname, '..', 'dist', '_locales'),
              context: 'public',
            },
            {
              from: path.resolve(__dirname, '..', 'src', 'images'),
              to: path.resolve(__dirname, '..', 'dist', 'images'),
              context: 'public',
            },
          ],
        }),
      ],
    };