diff --git a/config/webpack.config.js b/config/webpack.config.js index 64d82e714c453bda74f9310babfd3b895c8841c0..52cca8fa6dee997a0d1bc50003670834f3193130 100644 --- a/config/webpack.config.js +++ b/config/webpack.config.js @@ -42,9 +42,8 @@ const { WEBPACK_OUTPUT_PATH, WEBPACK_PUBLIC_PATH, SOURCEGRAPH_PUBLIC_PATH, - SOURCEGRAPH_OUTPUT_PATH, - GITLAB_WEB_IDE_OUTPUT_PATH, GITLAB_WEB_IDE_PUBLIC_PATH, + copyFilesPatterns, } = require('./webpack.constants'); const createIncrementalWebpackCompiler = require('./helpers/incremental_webpack_compiler'); @@ -89,9 +88,6 @@ if (WEBPACK_REPORT) { NO_HASHED_CHUNKS = true; } -const SOURCEGRAPH_PACKAGE = '@sourcegraph/code-host-integration'; -const GITLAB_WEB_IDE_PACKAGE = '@gitlab/web-ide'; - const devtool = IS_PRODUCTION ? 'source-map' : 'cheap-module-eval-source-map'; let autoEntriesCount = 0; @@ -711,34 +707,7 @@ module.exports = { }), new CopyWebpackPlugin({ - patterns: [ - { - from: path.join(ROOT_PATH, 'node_modules/pdfjs-dist/cmaps/'), - to: path.join(WEBPACK_OUTPUT_PATH, 'pdfjs/cmaps/'), - }, - { - from: path.join(ROOT_PATH, 'node_modules/pdfjs-dist/legacy/build/pdf.worker.min.js'), - to: path.join(WEBPACK_OUTPUT_PATH, 'pdfjs/'), - }, - { - from: path.join(ROOT_PATH, 'node_modules', SOURCEGRAPH_PACKAGE, '/'), - to: SOURCEGRAPH_OUTPUT_PATH, - globOptions: { - ignore: ['package.json'], - }, - }, - { - from: path.join(ROOT_PATH, 'node_modules', GITLAB_WEB_IDE_PACKAGE, 'dist', 'public'), - to: GITLAB_WEB_IDE_OUTPUT_PATH, - }, - { - from: path.join( - ROOT_PATH, - 'node_modules/@gitlab/visual-review-tools/dist/visual_review_toolbar.js', - ), - to: WEBPACK_OUTPUT_PATH, - }, - ], + patterns: copyFilesPatterns, }), // compression can require a lot of compute time and is disabled in CI diff --git a/config/webpack.constants.js b/config/webpack.constants.js index 0eb2ac70c82e5ee600b52148eb42986a39a0e182..fd9dd40a4c2d6117f7c39a8acb30be2deb569f01 100644 --- a/config/webpack.constants.js +++ b/config/webpack.constants.js @@ -18,14 +18,45 @@ const GITLAB_WEB_IDE_PUBLIC_PATH = path.join(WEBPACK_PUBLIC_PATH, GITLAB_WEB_IDE const IS_EE = require('./helpers/is_ee_env'); const IS_JH = require('./helpers/is_jh_env'); +const SOURCEGRAPH_PACKAGE = '@sourcegraph/code-host-integration'; +const GITLAB_WEB_IDE_PACKAGE = '@gitlab/web-ide'; + +const copyFilesPatterns = [ + { + from: path.join(ROOT_PATH, 'node_modules/pdfjs-dist/cmaps/'), + to: path.join(WEBPACK_OUTPUT_PATH, 'pdfjs/cmaps/'), + }, + { + from: path.join(ROOT_PATH, 'node_modules/pdfjs-dist/legacy/build/pdf.worker.min.js'), + to: path.join(WEBPACK_OUTPUT_PATH, 'pdfjs/'), + }, + { + from: path.join(ROOT_PATH, 'node_modules', SOURCEGRAPH_PACKAGE, '/'), + to: SOURCEGRAPH_OUTPUT_PATH, + globOptions: { + ignore: ['package.json'], + }, + }, + { + from: path.join(ROOT_PATH, 'node_modules', GITLAB_WEB_IDE_PACKAGE, 'dist', 'public'), + to: GITLAB_WEB_IDE_OUTPUT_PATH, + }, + { + from: path.join( + ROOT_PATH, + 'node_modules/@gitlab/visual-review-tools/dist/visual_review_toolbar.js', + ), + to: WEBPACK_OUTPUT_PATH, + }, +]; + module.exports = { IS_EE, IS_JH, ROOT_PATH, WEBPACK_OUTPUT_PATH, WEBPACK_PUBLIC_PATH, - SOURCEGRAPH_OUTPUT_PATH, SOURCEGRAPH_PUBLIC_PATH, - GITLAB_WEB_IDE_OUTPUT_PATH, GITLAB_WEB_IDE_PUBLIC_PATH, + copyFilesPatterns, }; diff --git a/package.json b/package.json index 5a1f9288be09ef4cb9444abc5c438af1b314cda8..7cb261f2e7102e48d64abee7bfe10fa1c939e075 100644 --- a/package.json +++ b/package.json @@ -267,6 +267,7 @@ "gettext-extractor": "^3.7.0", "gettext-extractor-vue": "^5.1.0", "glob": "^7.1.6", + "globby": "^11.0.1", "jest": "^28.1.3", "jest-canvas-mock": "^2.4.0", "jest-diff": "^28.1.3", diff --git a/vite.config.js b/vite.config.js index c74ef145b423db4b172ed6a1b8b467e5bae98d82..41344ca361a8518eb3304a14303a5206b050f878 100644 --- a/vite.config.js +++ b/vite.config.js @@ -1,4 +1,5 @@ import { readFileSync } from 'node:fs'; +import { stat, mkdir, copyFile } from 'node:fs/promises'; import path from 'node:path'; import { defineConfig } from 'vite'; @@ -6,6 +7,7 @@ import vue from '@vitejs/plugin-vue2'; import graphql from '@rollup/plugin-graphql'; import RubyPlugin from 'vite-plugin-ruby'; import chokidar from 'chokidar'; +import globby from 'globby'; import { viteCommonjs } from '@originjs/vite-plugin-commonjs'; import webpackConfig from './config/webpack.config'; import { @@ -13,6 +15,7 @@ import { IS_JH, SOURCEGRAPH_PUBLIC_PATH, GITLAB_WEB_IDE_PUBLIC_PATH, + copyFilesPatterns, } from './config/webpack.constants'; /* eslint-disable import/extensions */ import { viteCSSCompilerPlugin } from './scripts/frontend/lib/compile_css.mjs'; @@ -90,6 +93,76 @@ const autoRestartPlugin = { }, }; +/** + * This is a simple-reimplementation of the copy-webpack-plugin + * + * it also uses the `globby` package under the hood, and _only_ allows for copying + * 1. absolute paths + * 2. files and directories. + */ +function viteCopyPlugin({ patterns }) { + return { + name: 'viteCopyPlugin', + async configureServer() { + console.warn('Start copying files...'); + let count = 0; + + const allTheFiles = patterns.map(async (patternEntry) => { + const { from, to, globOptions = {} } = patternEntry; + + // By only supporting absolute paths we simplify + // the implementation a lot + if (!path.isAbsolute(from)) { + throw new Error(`'from' path is not absolute: ${path}`); + } + if (!path.isAbsolute(to)) { + throw new Error(`'to' path is not absolute: ${path}`); + } + + let pattern = ''; + let sourceRoot = ''; + const fromStat = await stat(from); + if (fromStat.isDirectory()) { + sourceRoot = from; + pattern = path.join(from, '**/*'); + } else if (fromStat.isFile()) { + sourceRoot = path.dirname(from); + pattern = from; + } else { + // No need to support globs, because we do not + // use them yet... + throw new Error('Our implementation does not support globs.'); + } + + globOptions.dot = globOptions.dot ?? true; + + const paths = await globby(pattern, globOptions); + + return paths.map((srcPath) => { + const targetPath = path.join(to, path.relative(sourceRoot, srcPath)); + return { srcPath, targetPath }; + }); + }); + + const srcTargetMap = (await Promise.all(allTheFiles)).flat(); + + await Promise.all( + srcTargetMap.map(async ({ srcPath, targetPath }) => { + try { + await mkdir(path.dirname(targetPath), { recursive: true }); + await copyFile(srcPath, targetPath); + count += 1; + } catch (e) { + console.warn(`Could not copy ${srcPath} => ${targetPath}`); + } + }), + ); + + console.warn(`Done copying ${count} files...`); + }, + }; +} + export default defineConfig({ cacheDir: path.resolve(__dirname, 'tmp/cache/vite'), resolve: { @@ -110,6 +183,9 @@ export default defineConfig({ plugins: [ viteCSSCompilerPlugin({ shouldWatch: viteGDKConfig.hmr !== null }), viteTailwindCompilerPlugin({ shouldWatch: viteGDKConfig.hmr !== null }), + viteCopyPlugin({ + patterns: copyFilesPatterns, + }), viteGDKConfig.enabled ? autoRestartPlugin : null, fixedRubyPlugin, vue({