postcss-loader causes change in how webpack recognizes modules
wibed opened this issue · 19 comments
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)
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
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?
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?
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
@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:
- Set
"module": "commonjs"
and override it forts-loader
- Put
"type": "module"
inpacakge.json
and useNODE_OPTIONS="--loader ts-node/esm" NODE_ENV=production webpack --mode=production
, note__dirname
is not avaliable - 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
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...
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
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.
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