From 9f8de7b5ec28bade9dd86a1fbaa39a11eb75541f Mon Sep 17 00:00:00 2001 From: "Grigorii K. Shartsev" Date: Mon, 12 Aug 2024 21:40:41 +0200 Subject: [PATCH] build: replace Webpack config with Vite config Signed-off-by: Grigorii K. Shartsev --- babel.config.js | 7 -- build-js/WebpackSPDXPlugin.js | 221 ---------------------------------- src/main.js | 5 - vite.config.mjs | 24 ++++ webpack.js | 51 -------- 5 files changed, 24 insertions(+), 284 deletions(-) delete mode 100644 babel.config.js delete mode 100644 build-js/WebpackSPDXPlugin.js create mode 100644 vite.config.mjs delete mode 100644 webpack.js diff --git a/babel.config.js b/babel.config.js deleted file mode 100644 index ce144aff..00000000 --- a/babel.config.js +++ /dev/null @@ -1,7 +0,0 @@ -/** - * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors - * SPDX-License-Identifier: AGPL-3.0-or-later - */ -const babelConfig = require('@nextcloud/babel-config') - -module.exports = babelConfig diff --git a/build-js/WebpackSPDXPlugin.js b/build-js/WebpackSPDXPlugin.js deleted file mode 100644 index eeb338c0..00000000 --- a/build-js/WebpackSPDXPlugin.js +++ /dev/null @@ -1,221 +0,0 @@ -'use strict' - -/** - * Party inspired by https://github.com/FormidableLabs/webpack-stats-plugin - * - * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors - * SPDX-License-Identifier: MIT - */ - -const { constants } = require('node:fs') -const fs = require('node:fs/promises') -const path = require('node:path') -const webpack = require('webpack') - -class WebpackSPDXPlugin { - - #options - - /** - * @param {object} opts Parameters - * @param {Record} opts.override Override licenses for packages - */ - constructor(opts = {}) { - this.#options = { override: {}, ...opts } - } - - apply(compiler) { - compiler.hooks.thisCompilation.tap('spdx-plugin', (compilation) => { - // `processAssets` is one of the last hooks before frozen assets. - // We choose `PROCESS_ASSETS_STAGE_REPORT` which is the last possible - // stage after which to emit. - compilation.hooks.processAssets.tapPromise( - { - name: 'spdx-plugin', - stage: compilation.constructor.PROCESS_ASSETS_STAGE_REPORT, - }, - () => this.emitLicenses(compilation), - ) - }) - } - - /** - * Find the nearest package.json - * @param {string} dir Directory to start checking - */ - async #findPackage(dir) { - if (!dir || dir === '/' || dir === '.') { - return null - } - - const packageJson = `${dir}/package.json` - try { - await fs.access(packageJson, constants.F_OK) - } catch (e) { - return await this.#findPackage(path.dirname(dir)) - } - - const { private: isPrivatePacket, name } = JSON.parse(await fs.readFile(packageJson)) - // "private" is set in internal package.json which should not be resolved but the parent package.json - // Same if no name is set in package.json - if (isPrivatePacket === true || !name) { - return (await this.#findPackage(path.dirname(dir))) ?? packageJson - } - return packageJson - } - - /** - * Emit licenses found in compilation to '.license' files - * @param {webpack.Compilation} compilation Webpack compilation object - * @param {*} callback Callback for old webpack versions - */ - async emitLicenses(compilation, callback) { - const logger = compilation.getLogger('spdx-plugin') - // cache the node packages - const packageInformation = new Map() - - const warnings = new Set() - /** @type {Map>} */ - const sourceMap = new Map() - - for (const chunk of compilation.chunks) { - for (const file of chunk.files) { - if (sourceMap.has(file)) { - sourceMap.get(file).add(chunk) - } else { - sourceMap.set(file, new Set([chunk])) - } - } - } - - for (const [asset, chunks] of sourceMap.entries()) { - /** @type {Set} */ - const modules = new Set() - /** - * @param {webpack.Module} module - */ - const addModule = (module) => { - if (module && !modules.has(module)) { - modules.add(module) - for (const dep of module.dependencies) { - addModule(compilation.moduleGraph.getModule(dep)) - } - } - } - chunks.forEach((chunk) => chunk.getModules().forEach(addModule)) - - const sources = [...modules].map((module) => module.identifier()) - .map((source) => { - const skipped = [ - 'delegated', - 'external', - 'container entry', - 'ignored', - 'remote', - 'data:', - ] - // Webpack sources that we can not infer license information or that is not included (external modules) - if (skipped.some((prefix) => source.startsWith(prefix))) { - return '' - } - // Internal webpack sources - if (source.startsWith('webpack/runtime')) { - return require.resolve('webpack') - } - // Handle webpack loaders - if (source.includes('!')) { - return source.split('!').at(-1) - } - if (source.includes('|')) { - return source - .split('|') - .filter((s) => s.startsWith(path.sep)) - .at(0) - } - return source - }) - .filter((s) => !!s) - .map((s) => s.split('?', 2)[0]) - - // Skip assets without modules, these are emitted by webpack plugins - if (sources.length === 0) { - logger.warn(`Skipping ${asset} because it does not contain any source information`) - continue - } - - /** packages used by the current asset - * @type {Set} - */ - const packages = new Set() - - // packages is the list of packages used by the asset - for (const sourcePath of sources) { - const pkg = await this.#findPackage(path.dirname(sourcePath)) - if (!pkg) { - logger.warn(`No package for source found (${sourcePath})`) - continue - } - - if (!packageInformation.has(pkg)) { - // Get the information from the package - const { author: packageAuthor, name, version, license: packageLicense, licenses } = JSON.parse(await fs.readFile(pkg)) - // Handle legacy packages - let license = !packageLicense && licenses - ? licenses.map((entry) => entry.type ?? entry).join(' OR ') - : packageLicense - if (license?.includes(' ') && !license?.startsWith('(')) { - license = `(${license})` - } - // Handle both object style and string style author - const author = typeof packageAuthor === 'object' - ? `${packageAuthor.name}` + (packageAuthor.mail ? ` <${packageAuthor.mail}>` : '') - : packageAuthor ?? `${name} developers` - - packageInformation.set(pkg, { - version, - // Fallback to directory name if name is not set - name: name ?? path.basename(path.dirname(pkg)), - author, - license, - }) - } - packages.add(pkg) - } - - let output = 'This file is generated from multiple sources. Included packages:\n' - const authors = new Set() - const licenses = new Set() - for (const packageName of [...packages].sort()) { - const pkg = packageInformation.get(packageName) - const license = this.#options.override[pkg.name] ?? pkg.license - // Emit warning if not already done - if (!license && !warnings.has(pkg.name)) { - logger.warn(`Missing license information for package ${pkg.name}, you should add it to the 'override' option.`) - warnings.add(pkg.name) - } - licenses.add(license || 'unknown') - authors.add(pkg.author) - output += `- ${pkg.name}\n\t- version: ${pkg.version}\n\t- license: ${license}\n` - } - output = `\n\n${output}` - for (const author of [...authors].sort()) { - output = `SPDX-FileCopyrightText: ${author}\n${output}` - } - for (const license of [...licenses].sort()) { - output = `SPDX-License-Identifier: ${license}\n${output}` - } - - compilation.emitAsset( - asset.split('?', 2)[0] + '.license', - new webpack.sources.RawSource(output), - ) - } - - if (callback) { - return callback() - } - } - -} - -module.exports = WebpackSPDXPlugin diff --git a/src/main.js b/src/main.js index 8c084411..e5273167 100644 --- a/src/main.js +++ b/src/main.js @@ -14,11 +14,6 @@ Vue.prototype.n = n Vue.prototype.OC = OC Vue.prototype.OCA = OCA -// eslint-disable-next-line -__webpack_nonce__ = btoa(OC.requestToken); -// eslint-disable-next-line -__webpack_public_path__ = OC.linkTo('notifications', 'js/'); - export default new Vue({ el: '#notifications', // eslint-disable-next-line vue/match-component-file-name diff --git a/vite.config.mjs b/vite.config.mjs new file mode 100644 index 00000000..1cf88634 --- /dev/null +++ b/vite.config.mjs @@ -0,0 +1,24 @@ +/** + * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +import { join, resolve } from 'node:path' +import { createAppConfig } from '@nextcloud/vite-config' + +export default createAppConfig({ + main: resolve(join('src', 'main.js')), + settings: resolve(join('src', 'settings.js')), + 'admin-settings': resolve(join('src', 'adminSettings.js')), +}, { + emptyOutputDirectory: { + additionalDirectories: [ + 'css', + ], + }, + extractLicenseInformation: { + overwriteLicenses: { + '@nextcloud/axios': 'GPL-3.0-or-later', + }, + }, +}) diff --git a/webpack.js b/webpack.js deleted file mode 100644 index 17231e18..00000000 --- a/webpack.js +++ /dev/null @@ -1,51 +0,0 @@ -/** - * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors - * SPDX-License-Identifier: AGPL-3.0-or-later - */ -const webpackConfig = require('@nextcloud/webpack-vue-config') -// eslint-disable-next-line n/no-extraneous-require -const TerserPlugin = require('terser-webpack-plugin') -const WebpackSPDXPlugin = require('./build-js/WebpackSPDXPlugin.js') -const webpackRules = require('@nextcloud/webpack-vue-config/rules') -const path = require('path') - -const BabelLoaderExcludeNodeModulesExcept = require('babel-loader-exclude-node-modules-except') - -// Edit JS rule -webpackRules.RULE_JS.exclude = BabelLoaderExcludeNodeModulesExcept([ - '@nextcloud/vue-richtext', - '@nextcloud/event-bus', - 'semver', -]) - -// Replaces rules array -webpackConfig.module.rules = Object.values(webpackRules) - -webpackConfig.entry = { - main: path.resolve(path.join('src', 'main.js')), - settings: path.resolve(path.join('src', 'settings.js')), - 'admin-settings': path.resolve(path.join('src', 'adminSettings.js')), -} - -webpackConfig.optimization.minimizer = [new TerserPlugin({ - extractComments: false, - terserOptions: { - format: { - comments: false, - }, - }, -})] - -webpackConfig.plugins = [ - ...webpackConfig.plugins, - // Generate reuse license files - new WebpackSPDXPlugin({ - override: { - // TODO: Remove if they fixed the license in the package.json - '@nextcloud/axios': 'GPL-3.0-or-later', - '@nextcloud/vue': 'AGPL-3.0-or-later', - 'nextcloud-vue-collections': 'AGPL-3.0-or-later', - } - }), -] -module.exports = webpackConfig