webpack-contrib / postcss-loader

PostCSS loader for webpack

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

postcss-loader causes change in how webpack recognizes modules

wibed opened this issue · comments

commented

installation of postcss-loader, causes the following error.
i can install postcss standalone just fine and it doesn't mess with the compilation.

error:

CMD: `rimraf dist && cross-env NODE_ENV=development webpack serve --mode=development`
[start:webpack] SyntaxError: Cannot use import statement outside a module
[start:webpack]     at internalCompileFunction (node:internal/vm:73:18)
[start:webpack]     at wrapSafe (node:internal/modules/cjs/loader:1176:20)
[start:webpack]     at Module._compile (node:internal/modules/cjs/loader:1218:27)
[start:webpack]     at Module.m._compile (/work/to/project/node_modules/ts-node/src/index.ts:1618:23)
[start:webpack]     at Module._extensions..js (node:internal/modules/cjs/loader:1308:10)
[start:webpack]     at Object.require.extensions.<computed> [as .ts] (/workspace/frickel/zoe4/node_modules/ts-node/src/index.ts:1621:12)
[start:webpack]     at Module.load (node:internal/modules/cjs/loader:1117:32)
[start:webpack]     at Function.Module._load (node:internal/modules/cjs/loader:958:12)
[start:webpack]     at Module.require (node:internal/modules/cjs/loader:1141:19)
[start:webpack]     at require (node:internal/modules/cjs/helpers:110:18)

the webpack config in question:

// client.config.ts
import type { Configuration as WebpackConfiguration } from 'webpack';
import { Configuration as WebpackDevServerConfiguration } from 'webpack-dev-server'

interface Configuration extends WebpackConfiguration {
  devServer?: WebpackDevServerConfiguration }

import {
  tsLoader,
  svgLoader,
  cssLoader,
  imageLoader,

  miniCSSPlugin,
  copyPlugin,
  htmlPlugin,
  htmlLoader,
  forkCheckerPlugin
} from './_lib';


import configuration from '../../../.config';


const clientConfiguration: Configuration = {
  name: "client",
  target: "web",
  entry: configuration.dir.src,
  devtool: configuration.is_dev ? "source-map" : false,
  devServer: {
    historyApiFallback: true,
    static: configuration.dir.dist,
    open: true,
    compress: true,
    host: configuration.client.host,
    port: configuration.client.port,
    devMiddleware: { writeToDisk: true }
  },
  output: {
    publicPath: '/',
    path: configuration.dir.dist,
    filename: '[name].bundle.js',
    assetModuleFilename: 'assets/[name][ext]'
  },
  resolve: {
    extensions: ['.js', '.jsx', '.ts', '.tsx'],
    alias: {
      react: configuration.alias_react,
      App: configuration.alias_app,
      assets: configuration.alias_assets,
      components: configuration.alias_components,
      features: configuration.alias_features,
      hooks: configuration.alias_hooks
    }
  },
  module: {
    rules: [
      tsLoader.client,
      svgLoader.client,
      cssLoader.client,
      imageLoader.client,
      htmlLoader.client
    ]
  },
  plugins: [
    miniCSSPlugin,
    copyPlugin,
    htmlPlugin,
    forkCheckerPlugin
  ],
  performance: {
    maxEntrypointSize: 512000,
    maxAssetSize: 512000
  },
  watchOptions: {
    aggregateTimeout: 500,
    poll: 1000,
  },
}

export default clientConfiguration

PS.:
deinstalling postcss-loader alone does not resolve the error.
i have to remove any related package aswell.

 npm r -D   tailwindcss   @tailwindcss/typography   autoprefixer   postcss   postcss-loader

It means your postcss configuration is in ESM and you load it using require:

[start:webpack] at require (node:internal/modules/cjs/helpers:110:18)

commented

i am not able to locate the import.

how would i approach this?

Sorry, hard to say, you miss the issue template, you can create reproducible test repo and I will look

commented

somehow react-scripts start works fine

but webpack serve --mode=development

does not

PS: i am not able to find the difference yet :/

Try to reinstall your deps, maybe configurations for typescript are different?

commented

i was wrong about react-scripts,

it creates an own implementation of webpack config, circumventing what i want to do..
use webpack as module with types.

heres a repro for you:

FROM node:latest

# install baseline
RUN npx create-react-app project --template redux-typescript
RUN npm -g i install-peerdeps

WORKDIR /project
RUN npm uninstall --save react-scripts

# setup tools
RUN npm i -D \
  rimraf \
  cross-env \
  dotenv \
  npm-run-all \
  buffer \
  base64-url \
  date-fns

# setup react
RUN npm i -S \
  react \
  react-dom \
  react-helmet \
  react-helmet-async \
  react-redux \
  react-router-dom \
  @reduxjs/toolkit \
  redux-logger \
  redux-thunk \
  jest

RUN npm i -D \
  @types/react \
  @types/react-dom \
  @types/react-helmet \
  @types/react-router-dom \
  @types/jest \
  @types/redux-logger

# setup compiler
RUN npm i -D \
  webpack \
  webpack-bundle-analyzer \
  webpack-cli \
  webpack-dev-server \
  webpack-dev-middleware \
  webpack-hot-middleware \
  dotenv-webpack \
  copy-webpack-plugin \
  eslint-webpack-plugin \
  fork-ts-checker-webpack-plugin \
  html-webpack-plugin \
  mini-css-extract-plugin \
  null-loader \
  terser-webpack-plugin \
  @svgr/webpack  \
  @types/webpack \
  @types/webpack-env \
  @types/webpack-bundle-analyzer \
  @types/webpack-dev-middleware \
  @types/webpack-hot-middleware 


# setup transpiler
RUN npm i -D \
  swc-loader \
  @swc/register \
  ts-loader \
  html-loader \
  css-loader 

# setup styles
RUN npm i -D \
  tailwindcss \
  @tailwindcss/typography \
  autoprefixer \
  material-ripple-effects \
  react-lorem-ipsum


RUN sed -i "s/\"scripts\": {/\"scripts\": {\n    \"build\": \"npm run clean \&\& cross-env NODE_ENV=production webpack --mode=production\"/" package.json
RUN sed -i "s/\"scripts\": {/\"scripts\": {\n    \"start\": \"npm run clean \&\& cross-env NODE_ENV=development webpack serve --mode=development\",/" package.json
RUN sed -i "s/\"scripts\": {/\"scripts\": {\n    \"prettier\": \"prettier \\\\\"src\/**\/*\\\\\" --write --single-quote --no-semi --ignore-unknown --trailing-comma none --jsx-single-quote\",/" package.json
RUN sed -i "s/\"scripts\": {/\"scripts\": {\n    \"lint\": \"ts-standard . \&\& stylelint **.*.{css}\",/" package.json
RUN sed -i "s/\"scripts\": {/\"scripts\": {\n    \"test\": \"jest\",/" package.json
RUN sed -i "s/\"scripts\": {/\"scripts\": {\n    \"clean\": \"rimraf dist\",/" package.json
RUN sed -i "/react-scripts/d" package.json

COPY ./webpack.config.ts ./webpack.config.ts
import path from 'path';

import type { Configuration as WebpackConfiguration } from 'webpack';
import { Configuration as WebpackDevServerConfiguration } from 'webpack-dev-server'

import MiniCssExtractPlugin from "mini-css-extract-plugin";
import HtmlWebpackPlugin from "html-webpack-plugin";

interface Configuration extends WebpackConfiguration {
  devServer?: WebpackDevServerConfiguration }


const clientConfiguration: Configuration = {
  name: "client",
  target: "es2022",
  entry: path.join(__dirname, "./src"),
  devtool: "source-map",
  devServer: {
    historyApiFallback: true,
    static: path.join(__dirname, "./dist"),
    open: true,
    compress: true,
    devMiddleware: { writeToDisk: true }
  },
  output: {
    publicPath: '/',
    path: path.join(__dirname, "./dist"),
    filename: '[name].bundle.js',
    assetModuleFilename: 'assets/[name][ext]',
    chunkFormat: 'commonjs'
  },
  resolve: {
    extensions: ['.js', '.jsx', '.ts', '.tsx'],
  },
  module: {
    rules: [
      {
        test: /\.(js|jsx|ts|tsx)$/,
        use: [
          { loader: "swc-loader" }
        ]
      },
      {
        test: /\.svg$/,
        use: ['@svgr/webpack']
      },
      {
        test: /\.(css)$/i,
        use: [
          { loader: MiniCssExtractPlugin.loader },
          {
            loader: 'css-loader',
            options: { importLoaders: 1 }
          }
        ]
      },
      {
        test:  /\.(gif|png|jpg|jpeg)$/i,
        type: 'asset'
      },
      {
        test: /\.html$/,
        use: [{ loader: "html-loader", options: { minimize: true } }]
      }
    ]
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: (
        true
        ? '[name].css' 
        : '[name].[contenthash].css'
      )
    }),
    new HtmlWebpackPlugin({
      template: "public/index.html",
      filename: "index.html",
    })
  ],
  performance: {
    maxEntrypointSize: 512000,
    maxAssetSize: 512000
  },
  watchOptions: {
    aggregateTimeout: 500,
    poll: 1000,
  },
}

export default clientConfiguration

Instructions:

1 . copy dockerfile and webpack config into same folder
2. docker build --tag repro .
3. docker run -it --rm --entrypoint /bin/bash repro
4. npm run start || you will find it works flawlessly
5. npm i -D postcss autoprefixer postcss-loader postcss-preset-env postcss-import postcss-flexbugs-fixes
6. npm run start || you will find above mentioned error message

i could narrow it down to postcss-loader, but dont know how it relates to the sourcecode. i hoped someone familiar with the codebase could point it out for me.

How you run webpack?

commented

How you run webpack?

idkwym.

the point is that webpack is used as a module here.
hence the import.
but this isnt possible as such as soon as postcss-loader is installed.

webpack itself is run by the start command in npm run start

Please put everything in github repo and post a link here, I will look and investigate

commented

@alexander-akait at your discretion

https://github.com/wibed/reproPostcss

download it,
run it --> it works

install packages
npm i -D postcss autoprefixer postcss-loader postcss-preset-env postcss-import postcss-flexbugs-fixes
run it --> it does not work

Thank you I will look soon

Okay, I found a problem, when you install postcss-loader (npm will install ts-node due by default peerDeps installation logic), after this your webpack configuration will load using ts-node due interpret logic inside webpack-cli (before it will not use ts-loader and use another package) - before you use @swc/register, when you run typescript on Node.js side I strongly recommend to setup your register explicit, otherwise you will run into it.

Also you have "module": "esnext",, but you don't have "type": "module" in package.json and it is a big problem. You can't use ESM code in CommonJs code.

Solutions:

  1. Set "module": "commonjs" and override it for ts-loader
  2. Put "type": "module" in pacakge.json and use NODE_OPTIONS="--loader ts-node/esm" NODE_ENV=production webpack --mode=production, note __dirname is not avaliable
  3. I tried to use @swc/register as loader, but looks like it doesn't support ESM right now (you can open an issue)

Why does it work before? Because there is a bug with pirates package (used in the hood of @swc/register) and they don't respect "module": "esnext" in tsconfig.js

commented

removed swc, and solely relied on ts-loader
added "type": "module" to package.json

and the following configuration in:

  "ts-node": {
    "esm": true,
    "experimentalSpecifierResolution": "node"
  }

aswell as removed "noEmit": true

but i now get the following error:

TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension ".ts" for /project/webpack.config.ts
    at new NodeError (node:internal/errors:399:5)
    at Object.getFileProtocolModuleFormat [as file:] (node:internal/modules/esm/get_format:99:9)
    at defaultGetFormat (node:internal/modules/esm/get_format:139:38)
    at defaultLoad (node:internal/modules/esm/load:83:20)
    at DefaultModuleLoader.load (node:internal/modules/esm/loader:319:26)
    at DefaultModuleLoader.moduleProvider (node:internal/modules/esm/loader:194:22)
    at new ModuleJob (node:internal/modules/esm/module_job:63:26)
    at DefaultModuleLoader.#createModuleJob (node:internal/modules/esm/loader:218:17)
    at DefaultModuleLoader.getJobFromResolveResult (node:internal/modules/esm/loader:171:34)
    at DefaultModuleLoader.getModuleJob (node:internal/modules/esm/loader:156:17) {
  code: 'ERR_UNKNOWN_FILE_EXTENSION'

i have updated the reproduction, at your discretion

PS:
i might add it works fine without postcss postcss-loader installed.
for some reason. i dont know why, if it falls back to ts-loader as previously stated

@wibed Many tools still ahve problems with ESM and typescript (you can find a lot of issues), my recommendation to keep configuration in ESM js format and use jsdocs for types, I know it is not perfect, but we can't do something here, for example postcss starts to support ts configuration only month ago, and it is still doesn't support ESM and we can't do something here...

commented

@alexander-akait

a possible fix could be to do as they did on here
lingui/js-lingui#1474

and replace https://github.com/Codex-/cosmiconfig-typescript-loader with a esm friendly typescript loader

@wibed Yeah, PR welcome, it looks easy

commented

i was experimenting with jiti and the postcss-loader source

and as i tried again it worked. heres a repro:
https://github.com/wibed/workingReproPostcss

yet past repro does not:
https://github.com/wibed/reproPostcss

i tried to diff -bur /reproPostcss /workingReproPostcss
and adjusting the settings to get it to break, but failed to do so.

i cant figure out what changed, the difference between the two, the reason for esm to fail on one and work on the other.
id appreciate another set of eyes to check it out and might tell me why one works and the other does not.

commented

it didnt work.

i havnt had swc installed.

adding ts-node: { ... swc: true ... } to the tsconfig.ts, while missing swc as a package,
prevented webpack from loading any postcss-loader code.
therefor appearing like it "worked".

on the other hand i got it working using the following in the scripts section:

{
  "start": "npm run clean && cross-env NODE_ENV=development NODE_OPTIONS=\"--loader=ts-node/esm --trace-warnings\" webpack serve --mode=development --config webpack.config.ts",
}

as suggested on here:
webpack/webpack-cli#2458

Yeah, we have a test case for ESM and ts and it works, but it doesn't work with mts right now due to porr tooling support it