Introduce vite (#2677)

Co-authored-by: Krzysztof Żuraw <9116238+krzysztofzuraw@users.noreply.github.com>
This commit is contained in:
Patryk Andrzejewski 2022-11-28 14:56:46 +01:00 committed by GitHub
parent ea2cd5fe96
commit aa10a1d08e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 2633 additions and 6199 deletions

View file

@ -15,9 +15,11 @@
"cypress", "cypress",
"chai-friendly", "chai-friendly",
"formatjs", "formatjs",
"react-hooks" "react-hooks",
"react-refresh"
], ],
"rules": { "rules": {
"react-refresh/only-export-components": "warn",
"@typescript-eslint/adjacent-overload-signatures": "error", "@typescript-eslint/adjacent-overload-signatures": "error",
"@typescript-eslint/array-type": [ "@typescript-eslint/array-type": [
"error", "error",

View file

@ -1,14 +1,16 @@
FROM node:18-alpine as builder FROM node:18-alpine as builder
WORKDIR /app WORKDIR /app
COPY package*.json ./ COPY package*.json ./
COPY scripts/patchReactVirtualized.js scripts/
RUN npm ci --legacy-peer-deps RUN npm ci --legacy-peer-deps
COPY nginx/ nginx/ COPY nginx/ nginx/
COPY assets/ assets/ COPY assets/ assets/
COPY locale/ locale/ COPY locale/ locale/
COPY testUtils testUtils/ COPY testUtils testUtils/
COPY scripts/removeSourcemaps.js scripts/
COPY codegen.yml ./ COPY codegen.yml ./
COPY webpack.config.js ./ COPY vite.config.js ./
COPY tsconfig.json ./ COPY tsconfig.json ./
COPY *.d.ts ./ COPY *.d.ts ./
COPY schema.graphql ./ COPY schema.graphql ./

View file

@ -86,7 +86,7 @@ Create `.env` file in a root directory or set environment variables with followi
To start the development server run: To start the development server run:
``` ```
$ npm start $ npm run dev
``` ```
In case you see CORS errors make sure to check [CORS configuration](https://docs.saleor.io/docs/3.x/developer/running-saleor/configuration#allowed_client_hosts) of your Saleor instance or CORS settings in the Cloud Console. In case you see CORS errors make sure to check [CORS configuration](https://docs.saleor.io/docs/3.x/developer/running-saleor/configuration#allowed_client_hosts) of your Saleor instance or CORS settings in the Cloud Console.

8421
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -173,7 +173,6 @@
"file-loader": "^5.0.2", "file-loader": "^5.0.2",
"fork-ts-checker-webpack-plugin": "^3.1.1", "fork-ts-checker-webpack-plugin": "^3.1.1",
"graphql-request": "^3.7.0", "graphql-request": "^3.7.0",
"html-webpack-plugin": "^3.2.0",
"husky": "^4.3.0", "husky": "^4.3.0",
"identity-obj-proxy": "^3.0.0", "identity-obj-proxy": "^3.0.0",
"jest": "^24.8.0", "jest": "^24.8.0",
@ -195,22 +194,20 @@
"require-context.macro": "^1.1.1", "require-context.macro": "^1.1.1",
"rimraf": "^3.0.0", "rimraf": "^3.0.0",
"setup-polly-jest": "^0.9.1", "setup-polly-jest": "^0.9.1",
"speed-measure-webpack-plugin": "^1.5.0",
"start-server-and-test": "^1.11.0", "start-server-and-test": "^1.11.0",
"ts-jest": "^27.0.7", "ts-jest": "^27.0.7",
"tsconfig-paths-webpack-plugin": "^3.2.0",
"typescript": "^4.8.4", "typescript": "^4.8.4",
"webpack": "^4.35.3",
"webpack-bundle-analyzer": "^4.4.1",
"webpack-cli": "^3.3.6",
"webpack-dev-server": "^3.11.0",
"webpack-manifest-plugin": "2.2.0",
"workbox-cacheable-response": "^6.1.2", "workbox-cacheable-response": "^6.1.2",
"workbox-expiration": "^6.1.2", "workbox-expiration": "^6.1.2",
"workbox-precaching": "^6.1.2", "workbox-precaching": "^6.1.2",
"workbox-routing": "^6.1.2", "workbox-routing": "^6.1.2",
"workbox-strategies": "^6.1.2", "workbox-strategies": "^6.1.2",
"workbox-webpack-plugin": "^6.1.2" "vite": "^3.2.4",
"vite-plugin-html": "^3.2.0",
"vite-plugin-sentry": "^1.1.6",
"vite-plugin-swc-react-refresh": "^2.2.1",
"eslint-plugin-react-refresh": "^0.3.1",
"tsconfig-paths-webpack-plugin": "^3.2.0"
}, },
"optionalDependencies": { "optionalDependencies": {
"fsevents": "^1.2.9" "fsevents": "^1.2.9"
@ -255,6 +252,9 @@
"pre-push": "npm run check-types" "pre-push": "npm run check-types"
} }
}, },
"overrides": {
"resolve": "1.20.0"
},
"lint-staged": { "lint-staged": {
"*.{ts,tsx}": [ "*.{ts,tsx}": [
"eslint --fix", "eslint --fix",
@ -266,10 +266,12 @@
] ]
}, },
"scripts": { "scripts": {
"dev": "vite --host",
"build": "cross-env NODE_OPTIONS=--max_old_space_size=16384 vite build",
"preview": "vite preview",
"build-storybook": "cross-env NODE_OPTIONS=--openssl-legacy-provider build-storybook -c src/storybook/ -o build/storybook", "build-storybook": "cross-env NODE_OPTIONS=--openssl-legacy-provider build-storybook -c src/storybook/ -o build/storybook",
"build-types": "graphql-codegen", "build-types": "graphql-codegen",
"prebuild": "npm run build-types", "prebuild": "npm run build-types",
"build": "cross-env NODE_OPTIONS=--openssl-legacy-provider webpack -p",
"check-strict-null-errors": "tsc --noEmit --strictNullChecks | node scripts/count-strict-null-check-errors.js", "check-strict-null-errors": "tsc --noEmit --strictNullChecks | node scripts/count-strict-null-check-errors.js",
"check-types": "tsc --noEmit", "check-types": "tsc --noEmit",
"extract-json-messages": "formatjs extract 'src/**/*.{ts,tsx}' --out-file locale/defaultMessages.json --format scripts/formatter.js", "extract-json-messages": "formatjs extract 'src/**/*.{ts,tsx}' --out-file locale/defaultMessages.json --format scripts/formatter.js",
@ -278,7 +280,6 @@
"heroku-postbuild": "npm run build", "heroku-postbuild": "npm run build",
"serve:lhci": "cross-env NODE_ENV=production npm run server", "serve:lhci": "cross-env NODE_ENV=production npm run server",
"prestart": "npm run build-types", "prestart": "npm run build-types",
"start": "cross-env NODE_OPTIONS=--openssl-legacy-provider webpack-dev-server -d",
"storybook": "cross-env NODE_OPTIONS=--openssl-legacy-provider start-storybook -p 3000 -c src/storybook/", "storybook": "cross-env NODE_OPTIONS=--openssl-legacy-provider start-storybook -p 3000 -c src/storybook/",
"cy:run": "cypress run", "cy:run": "cypress run",
"cy:run:dashboard": "cypress run --record", "cy:run:dashboard": "cypress run --record",
@ -288,10 +289,12 @@
"cy:run:critical:parallel": "cypress run --record --env grepTags=@critical --parallel --tag Critical", "cy:run:critical:parallel": "cypress run --record --env grepTags=@critical --parallel --tag Critical",
"cy:run:allEnv:parallel": "cypress run --record --env grepTags=@allEnv --parallel", "cy:run:allEnv:parallel": "cypress run --record --env grepTags=@allEnv --parallel",
"cy:run:stable:parallel": "cypress run --record --env grepTags=@critical --parallel --tag Stable", "cy:run:stable:parallel": "cypress run --record --env grepTags=@critical --parallel --tag Stable",
"test": "TZ=UTC jest src/", "test": "TZ=UTC NODE_ENV=test jest src/",
"lint": "npx eslint \"src/**/*.@(tsx|ts|jsx|js)\" --fix ; npx prettier --check \"src/**/*.@(tsx|ts|jsx|js)\" --write", "lint": "npx eslint \"src/**/*.@(tsx|ts|jsx|js)\" --fix ; npx prettier --check \"src/**/*.@(tsx|ts|jsx|js)\" --write",
"postbuild": "rimraf ./build/**/*.js.map", "postbuild": "node scripts/removeSourcemaps.js",
"postinstall": "node scripts/patchReactVirtualized.js",
"predev": "npm run build-types",
"release": "release-it" "release": "release-it"
}, },
"description": "![Saleor Dashboard](https://user-images.githubusercontent.com/249912/82305745-5c52fd00-99be-11ea-9ac6-cc04a6f28c91.png)" "description": "![Saleor Dashboard](https://user-images.githubusercontent.com/249912/82305745-5c52fd00-99be-11ea-9ac6-cc04a6f28c91.png)"
} }

View file

@ -0,0 +1,18 @@
/* eslint-disable @typescript-eslint/no-var-requires */
const fs = require("fs");
/*
react-virtualized has broken ESM file, we need to patch this manually:
Ref: https://github.com/vitejs/vite/issues/1652#issuecomment-765875146
*/
const dep =
"node_modules/react-virtualized/dist/es/WindowScroller/utils/onScroll.js";
const code = fs
.readFileSync(dep, "utf-8")
.replace(
'import { bpfrpt_proptype_WindowScroller } from "../WindowScroller.js";',
"",
);
fs.writeFileSync(dep, code);

View file

@ -0,0 +1,16 @@
/* eslint-disable @typescript-eslint/no-var-requires */
/* eslint-disable no-console */
const rimraf = require("rimraf");
/*
When the sentry is enabled, remove sourcemaps
*/
if (
process.env.SENTRY_ORG &&
process.env.SENTRY_PROJECT &&
process.env.SENTRY_DSN &&
process.env.SENTRY_AUTH_TOKEN
) {
rimraf("./build/**/*.js.map", () => console.log("Source maps removed."));
}

View file

@ -1,6 +1,8 @@
import { import {
ApolloError, ApolloError,
ApolloQueryResult, ApolloQueryResult,
LazyQueryHookOptions as BaseLazyQueryHookOptions,
OperationVariables,
QueryHookOptions as BaseQueryHookOptions, QueryHookOptions as BaseQueryHookOptions,
QueryResult, QueryResult,
useQuery as useBaseQuery, useQuery as useBaseQuery,
@ -18,7 +20,7 @@ import { useIntl } from "react-intl";
import useAppState from "./useAppState"; import useAppState from "./useAppState";
import useNotifier from "./useNotifier"; import useNotifier from "./useNotifier";
export { useLazyQuery, LazyQueryHookOptions } from "@apollo/client"; export { useLazyQuery } from "@apollo/client";
const getPermissionKey = (permission: string) => const getPermissionKey = (permission: string) =>
`PERMISSION_${permission}` as PrefixedPermissions; `PERMISSION_${permission}` as PrefixedPermissions;
@ -51,6 +53,11 @@ export interface LoadMore<TData, TVariables> {
) => Promise<ApolloQueryResult<TData>>; ) => Promise<ApolloQueryResult<TData>>;
} }
export type LazyQueryHookOptions<
TData = any,
TVariables = OperationVariables
> = BaseLazyQueryHookOptions<TData, TVariables>;
export type UseQueryResult<TData, TVariables> = QueryResult<TData, TVariables> & export type UseQueryResult<TData, TVariables> = QueryResult<TData, TVariables> &
LoadMore<TData, TVariables>; LoadMore<TData, TVariables>;
export type QueryHookOptions<TData, TVariables> = Partial< export type QueryHookOptions<TData, TVariables> = Partial<
@ -60,6 +67,7 @@ export type QueryHookOptions<TData, TVariables> = Partial<
variables?: Omit<TVariables, PrefixedPermissions>; variables?: Omit<TVariables, PrefixedPermissions>;
} }
>; >;
type UseQueryHook<TData, TVariables> = ( type UseQueryHook<TData, TVariables> = (
opts?: QueryHookOptions<TData, Omit<TVariables, PrefixedPermissions>>, opts?: QueryHookOptions<TData, Omit<TVariables, PrefixedPermissions>>,
) => UseQueryResult<TData, TVariables>; ) => UseQueryResult<TData, TVariables>;

View file

@ -10,8 +10,8 @@
<title>Saleor e-commerce</title> <title>Saleor e-commerce</title>
<script> <script>
window.__SALEOR_CONFIG__ = { window.__SALEOR_CONFIG__ = {
API_URL: "<%= API_URL %>", API_URL: "<%- API_URL %>",
APP_MOUNT_URI: "<%= APP_MOUNT_URI %>", APP_MOUNT_URI: "<%- APP_MOUNT_URI %>",
}; };
</script> </script>
</head> </head>

View file

138
vite.config.js Normal file
View file

@ -0,0 +1,138 @@
/* eslint-disable no-console */
import path from "path";
import { defineConfig, loadEnv } from "vite";
import { createHtmlPlugin } from "vite-plugin-html";
import viteSentry from "vite-plugin-sentry";
import { swcReactRefresh } from "vite-plugin-swc-react-refresh";
export default defineConfig(({ command, mode }) => {
const env = loadEnv(mode, process.cwd(), "");
/*
Using explicit env variables, there is no need to expose all of them (security).
*/
const {
NODE_ENV,
API_URI,
SW_INTERVAL,
IS_CLOUD_INSTANCE,
MARKETPLACE_URL,
SALEOR_APPS_ENDPOINT,
APP_MOUNT_URI,
SENTRY_ORG,
SENTRY_PROJECT,
SENTRY_AUTH_TOKEN,
SENTRY_DSN,
ENVIRONMENT,
STATIC_URL,
} = env;
const enableSentry =
SENTRY_ORG && SENTRY_PROJECT && SENTRY_DSN && SENTRY_AUTH_TOKEN;
const plugins = [
swcReactRefresh(),
createHtmlPlugin({
entry: "/index.tsx",
template: "index.html",
inject: {
data: {
API_URL: API_URI,
APP_MOUNT_URI,
},
},
}),
];
if (enableSentry) {
console.log("Enabling sentry...");
plugins.push(
viteSentry({
sourceMaps: {
include: "./build/dashboard",
urlPrefix: process.env.SENTRY_URL_PREFIX,
},
}),
);
}
/*
"qs" package uses 'get-intrinsic' whish refers to the global object, we need to recreate it.
Issue presents only on development mode.
*/
const globals = command !== "build" ? { global: {} } : {};
return {
root: "src",
base: STATIC_URL ?? "/",
envDir: "..",
server: {
port: 9000,
},
define: {
...globals,
/*
We still have references to process.env, we need to peserve them as workaround.
*/
"process.env": {
NODE_ENV,
API_URI,
SW_INTERVAL,
IS_CLOUD_INSTANCE,
MARKETPLACE_URL,
SALEOR_APPS_ENDPOINT,
APP_MOUNT_URI,
SENTRY_DSN,
ENVIRONMENT,
},
},
build: {
minify: false,
sourcemap: true,
emptyOutDir: true,
outDir: "../build/dashboard",
assetsDir: ".",
commonjsOptions: {
/*
Fix dynamic imports by "require", Necessary for react-editor-js
Ref: https://github.com/Jungwoo-An/react-editor-js/blob/e58b7ba5e66d07912bb78f65ac911e4018d363e1/packages/react-editor-js/src/factory.ts#L5
*/
transformMixedEsModules: true,
},
},
optimizeDeps: {
include: ["esm-dep > cjs-dep"],
},
resolve: {
alias: {
/*
Aliases from tsconfig, we need to preserve them.
Since @saleor points to "src", we need to add aliases for macaw and sdk as first.
*/
"@saleor/macaw-ui": path.resolve(
__dirname,
"node_modules/@saleor/macaw-ui",
),
"@saleor/app-sdk": path.resolve(
__dirname,
"node_modules/@saleor/app-sdk",
),
"@saleor/sdk": path.resolve(__dirname, "node_modules/@saleor/sdk"),
"@saleor": path.resolve(__dirname, "./src"),
"@assets": path.resolve(__dirname, "./assets"),
"@locale": path.resolve(__dirname, "./locale"),
src: path.resolve(__dirname, "./src"),
/*
Moment.js/react-moment does not fully suport ES modules.
Vite resolves it by using jsnext:main https://github.com/moment/moment/blob/develop/package.json#L26.
We enforce to use a different path, ignoring jsnext:main field.
*/
moment: path.resolve(__dirname, "./node_modules/moment/moment.js"),
},
},
plugins,
esbuild: { jsx: "automatic" },
};
});

View file

@ -1,184 +0,0 @@
/* eslint-disable @typescript-eslint/no-var-requires */
const path = require("path");
const CheckerPlugin = require("fork-ts-checker-webpack-plugin");
const webpack = require("webpack");
const TsconfigPathsPlugin = require("tsconfig-paths-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const { InjectManifest } = require("workbox-webpack-plugin");
const SentryWebpackPlugin = require("@sentry/webpack-plugin");
const BundleAnalyzerPlugin = require("webpack-bundle-analyzer")
.BundleAnalyzerPlugin;
const SpeedMeasurePlugin = require("speed-measure-webpack-plugin");
require("dotenv").config();
const resolve = path.resolve.bind(path, __dirname);
let bundleAnalyzerPlugin;
let speedMeasureWrapper = fn => fn;
const analyze = process.env.ANALYZE;
if (!!analyze) {
const smp = new SpeedMeasurePlugin();
speedMeasureWrapper = smp.wrap;
bundleAnalyzerPlugin = new BundleAnalyzerPlugin();
}
const pathsPlugin = new TsconfigPathsPlugin({
configFile: "./tsconfig.json",
});
const checkerPlugin = new CheckerPlugin({
eslint: true,
reportFiles: ["src/**/*.{ts,tsx}"],
});
const htmlWebpackPlugin = new HtmlWebpackPlugin({
filename: "index.html",
hash: true,
template: "./src/index.html",
templateParameters: {
// URI is kept for backwards compatibility.
// See more at https://github.com/saleor/saleor-dashboard/issues/2502
API_URL: process.env.API_URI,
APP_MOUNT_URI: process.env.APP_MOUNT_URI,
},
});
const environmentPlugin = new webpack.EnvironmentPlugin({
MARKETPLACE_URL: "",
SALEOR_APPS_ENDPOINT: "",
DEMO_MODE: false,
ENVIRONMENT: "",
GTM_ID: "",
SENTRY_DSN: "",
SW_INTERVAL: "300", // Fetch SW every 300 seconds
IS_CLOUD_INSTANCE: false,
});
const dashboardBuildPath = "build/dashboard/";
module.exports = speedMeasureWrapper((env, argv) => {
const devMode = argv.mode !== "production";
let fileLoaderPath;
let output;
if (!process.env.API_URI) {
throw new Error("Environment variable API_URI not set");
}
const publicPath = process.env.STATIC_URL || "/";
if (!devMode) {
output = {
chunkFilename: "[name].[chunkhash].js",
filename: "[name].[chunkhash].js",
path: resolve(dashboardBuildPath),
publicPath,
};
fileLoaderPath = "file-loader?name=[name].[hash].[ext]";
} else {
output = {
chunkFilename: "[name].js",
filename: "[name].js",
path: resolve(dashboardBuildPath),
publicPath,
};
fileLoaderPath = "file-loader?name=[name].[ext]";
}
// Create release if sentry config is set
let sentryPlugin;
if (
!devMode &&
process.env.SENTRY_ORG &&
process.env.SENTRY_PROJECT &&
process.env.SENTRY_DSN &&
process.env.SENTRY_AUTH_TOKEN
) {
sentryPlugin = new SentryWebpackPlugin({
include: "./build/dashboard/",
urlPrefix: process.env.SENTRY_URL_PREFIX,
});
}
let manifestPlugin;
if (!devMode) {
manifestPlugin = new InjectManifest({
swSrc: "./src/sw.js",
swDest: "sw.js",
maximumFileSizeToCacheInBytes: 5000000,
webpackCompilationPlugins: [checkerPlugin],
});
}
return {
devServer: {
compress: true,
contentBase: path.join(__dirname, dashboardBuildPath),
historyApiFallback: true,
hot: true,
port: 9000,
},
devtool: devMode ? "cheap-module-source-map" : "source-map",
entry: {
dashboard: "./src/index.tsx",
},
module: {
rules: [
{
test: /\.(jsx?|tsx?)$/,
use: [
{
loader: "esbuild-loader",
options: {
loader: "tsx",
target: "es2015",
},
},
],
},
{
test: /\.css$/i,
use: ["style-loader", "css-loader"],
},
{
include: [
resolve("node_modules"),
resolve("assets/fonts"),
resolve("assets/images"),
resolve("assets/favicons"),
],
loader: fileLoaderPath,
test: /\.(eot|otf|png|svg|jpg|ttf|woff|woff2)(\?v=[0-9.]+)?$/,
},
],
},
optimization: {
removeAvailableModules: false,
removeEmptyChunks: false,
splitChunks: false,
},
output,
plugins: [
checkerPlugin,
environmentPlugin,
htmlWebpackPlugin,
sentryPlugin,
manifestPlugin,
bundleAnalyzerPlugin,
].filter(Boolean),
resolve: {
// Resolve macaw ui's peer dependencies to our own node_modules
// to make it work with npm link
alias: {
react: path.resolve("./node_modules/react"),
"react-dom": path.resolve("./node_modules/react-dom"),
"@material-ui/core": path.resolve("./node_modules/@material-ui/core"),
"@material-ui/icons": path.resolve("./node_modules/@material-ui/icons"),
"@material-ui/styles": path.resolve(
"./node_modules/@material-ui/styles",
),
},
extensions: [".js", ".jsx", ".ts", ".tsx"],
plugins: [pathsPlugin],
},
};
});