Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5,301 changes: 4,035 additions & 1,266 deletions package-lock.json

Large diffs are not rendered by default.

24 changes: 16 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,22 @@
"dist"
],
"scripts": {
"build": "NODE_ENV=production NODE_OPTIONS='--max-old-space-size=4096' vite --mode production build",
"dev": "NODE_ENV=development NODE_OPTIONS='--max-old-space-size=4096' vite --mode development build",
"analyze": "rspack build --env production --analyze",
"analyze:stats": "rspack build --env production --json js/stats.json",
"build": "NODE_ENV=production rspack build --mode=production",
"dev": "NODE_ENV=development rspack build --mode=development",
"lint": "tsc && ESLINT_USE_FLAT_CONFIG=false eslint --ext .js,.ts,.vue src cypress playwright",
"lint:fix": "tsc && ESLINT_USE_FLAT_CONFIG=false eslint --ext .js,.ts,.vue src cypress playwright --fix",
"prettier": "prettier --check .",
"prettier:change": "git diff HEAD --name-only | xargs prettier --write --no-error-on-unmatched-pattern",
"prettier:fix": "prettier --write .",
"serve": "BASE=${BASE:-/apps/text} NODE_ENV=development vite --mode development serve --host",
"serve": "NODE_ENV=development rspack serve",
"start:nextcloud": "node playwright/start-nextcloud-server.mjs",
"test": "NODE_ENV=test vitest run",
"test:coverage": "NODE_ENV=test vitest run --coverage",
"test:cypress": "cd cypress && ./runLocal.sh run",
"test:cypress:open": "cd cypress && ./runLocal.sh open",
"watch": "NODE_ENV=development NODE_OPTIONS='--max-old-space-size=8192' vite --mode development build --watch"
"watch": "NODE_ENV=development rspack build --mode=development --watch"
},
"browserslist": [
"extends @nextcloud/browserslist-config"
Expand Down Expand Up @@ -110,14 +112,18 @@
"@nextcloud/e2e-test-server": "^0.4.0",
"@nextcloud/eslint-config": "^8.4.2",
"@nextcloud/prettier-config": "^1.2.0",
"@nextcloud/vite-config": "^1.7.2",
"@playwright/test": "^1.57.0",
"@rspack/cli": "^1.1.8",
"@rspack/core": "^1.1.8",
"@rspack/plugin-node-polyfill": "^0.5.8",
"@types/markdown-it": "^14.1.2",
"@vitejs/plugin-vue2": "^2.3.4",
"@vitest/coverage-v8": "^4.0.16",
"@vue/test-utils": "^1.3.0 <2",
"@vue/tsconfig": "^0.5.1",
"@vueuse/core": "^11.3.0",
"browserslist": "^4.24.4",
"css-loader": "^7.1.2",
"cypress": "^15.8.1",
"cypress-split": "^1.24.25",
"cypress-vite": "^1.8.0",
Expand All @@ -127,12 +133,14 @@
"jsdom": "^27.4.0",
"prettier-plugin-organize-imports": "^4.3.0",
"prosemirror-test-builder": "^1.1.1",
"rollup-plugin-webpack-stats": "^2.1.8",
"resolve-url-loader": "^5.0.0",
"sass": "^1.83.4",
"sass-loader": "^16.0.4",
"typescript": "^5.9.3",
"vite": "^7.3.0",
"vite-plugin-commonjs": "^0.10.4",
"vite-plugin-node-polyfills": "^0.24.0",
"vitest": "^4.0.16",
"vue-demi": "^0.14.10",
"vue-loader": "^15.11.1",
"vue-template-compiler": "^2.7.16",
"vue-tsc": "^2.2.12"
},
Expand Down
3 changes: 1 addition & 2 deletions playwright/e2e/editor-api-create-table.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@ test.describe('createTable API', () => {

// Load the editor API bundle
await page.addScriptTag({
url: '/apps/text/js/text-editor.mjs',
type: 'module',
url: '/apps/text/js/text-editor.js',
})
})

Expand Down
6 changes: 2 additions & 4 deletions playwright/e2e/editor-api.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@ test.describe('editor API at window.OCA.Text - MarkdownContentEditor.vue without

// Load the editor API bundle
await page.addScriptTag({
url: '/apps/text/js/text-editor.mjs',
type: 'module',
url: '/apps/text/js/text-editor.js',
})
})

Expand Down Expand Up @@ -67,8 +66,7 @@ fileTest.describe(

// Load the editor API bundle
await page.addScriptTag({
url: '/apps/text/js/text-editor.mjs',
type: 'module',
url: '/apps/text/js/text-editor.js',
})
})

Expand Down
288 changes: 288 additions & 0 deletions rspack.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,288 @@
/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

import browserslistConfig from '@nextcloud/browserslist-config'
import { defineConfig } from '@rspack/cli'
import {
CssExtractRspackPlugin,
DefinePlugin,
LightningCssMinimizerRspackPlugin,
ProgressPlugin,
SwcJsMinimizerRspackPlugin,
} from '@rspack/core'
import NodePolyfillPlugin from '@rspack/plugin-node-polyfill'
import browserslist from 'browserslist'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
import { VueLoaderPlugin } from 'vue-loader'

const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)

// browserslist-rs does not support baseline queries yet
// Manually resolving the browserslist config to the list of browsers with minimal versions
// See: https://github.com/browserslist/browserslist-rs/issues/40
const browsers = browserslist(browserslistConfig)
const minBrowserVersion = browsers
.map((str) => str.split(' '))
.reduce((minVersion, [browser, version]) => {
minVersion[browser] = minVersion[browser]
? Math.min(minVersion[browser], parseFloat(version))
: parseFloat(version)
return minVersion
}, {})
const targets = Object.entries(minBrowserVersion)
.map(([browser, version]) => `${browser} >=${version}`)
.join(',')

/** @type {import('@rspack/cli').RspackConfigFn} */
const config = (env) => {
const appName = process.env.npm_package_name
const appVersion = process.env.npm_package_version

const mode =
(env.development && 'development')
|| (env.production && 'production')
|| process.env.NODE_ENV
|| 'production'
const isDev = mode === 'development'
process.env.NODE_ENV = mode

console.info('Building', appName, appVersion, '\n')

return {
target: 'web',
mode,
devtool: isDev ? 'cheap-source-map' : 'source-map',

entry: {
text: path.join(__dirname, 'src', 'main.js'),
files: path.join(__dirname, 'src', 'files.ts'),
public: path.join(__dirname, 'src', 'public.js'),
viewer: path.join(__dirname, 'src', 'viewer.js'),
editor: path.join(__dirname, 'src', 'editor.js'),
init: path.join(__dirname, 'src', 'init.js'),
},

output: {
path: path.resolve('./js'),
filename: `${appName}-[name].js?v=[contenthash]`,
chunkFilename: `${appName}-[name].js?v=[contenthash]`,
// Set publicPath via __webpack_public_path__
publicPath: 'auto',
assetModuleFilename: '[name][ext]?v=[contenthash]',
clean: true,
devtoolNamespace: appName,
// Make sure sourcemaps have a proper path and do not leak local paths
devtoolModuleFilenameTemplate(info) {
const rootDir = process.cwd()
const rel = path.relative(rootDir, info.absoluteResourcePath)
return `webpack:///${appName}/${rel}`
},
},

devServer: {
hot: true,
host: '127.0.0.1',
port: 3000,
allowedHosts: ['host.docker.internal', 'localhost', 'nextcloud.local'],
client: {
overlay: false,
},
devMiddleware: {
writeToDisk: true,
},
headers: {
'Access-Control-Allow-Origin': '*',
},
},

optimization: {
chunkIds: 'named',
splitChunks: {
automaticNameDelimiter: '-',
cacheGroups: {
defaultVendors: {
reuseExistingChunk: true,
},
// Make the emoji related dependencies a custom chunk
'emoji-picker': {
test: /[\\/]node_modules[\\/](emoji-mart-vue|emoji-datasource)/,
name: 'emoji-picker',
chunks: 'async',
priority: 20,
},
},
},
minimize: !isDev,
minimizer: [
new SwcJsMinimizerRspackPlugin({
minimizerOptions: {
targets,
},
}),
new LightningCssMinimizerRspackPlugin({
minimizerOptions: {
targets,
},
}),
],
},

module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader',
options: {
experimentalInlineMatchResource: true,
},
},
{
test: /\.css$/,
use: [
{
loader: CssExtractRspackPlugin.loader,
},
{
loader: 'css-loader',
options: {
modules: {
auto: true,
localIdentName: isDev
? '[path][name]__[local]'
: '[hash:base64]',
exportLocalsConvention: 'camelCase',
},
url: {
filter: (url) => {
// Let rspack handle absolute paths and data URLs
return (
!url.startsWith('/')
&& !url.startsWith('data:')
)
},
},
},
},
],
},
{
test: /\.scss$/,
use: [
{
loader: CssExtractRspackPlugin.loader,
},
{
loader: 'css-loader',
options: {
modules: {
auto: true,
localIdentName: isDev
? '[path][name]__[local]'
: '[hash:base64]',
exportLocalsConvention: 'camelCase',
},
url: {
filter: (url) => {
// Let rspack handle absolute paths and data URLs
return (
!url.startsWith('/')
&& !url.startsWith('data:')
)
},
},
},
},
{
loader: 'resolve-url-loader',
options: {
sourceMap: true,
},
},
{
loader: 'sass-loader',
options: {
sourceMap: true,
sassOptions: {
includePaths: [path.resolve(__dirname)],
},
},
},
],
},
{
test: /\.ts$/,
exclude: [/node_modules/],
loader: 'builtin:swc-loader',
options: {
jsc: {
parser: {
syntax: 'typescript',
},
},
env: {
targets,
},
},
type: 'javascript/auto',
},
{
test: /\.(png|jpe?g|gif|svg|webp)$/i,
type: 'asset',
},
{
test: /\.(woff2?|eot|ttf|otf)$/i,
type: 'asset/resource',
},
{
resourceQuery: /raw/,
type: 'asset/source',
},
],
},

plugins: [
new ProgressPlugin(),

new VueLoaderPlugin(),

new NodePolyfillPlugin(),

new DefinePlugin({
appName: JSON.stringify(appName),
appVersion: JSON.stringify(appVersion),
// Vue 2 compile time flags
__VUE_OPTIONS_API__: true,
__VUE_PROD_DEVTOOLS__: false,
}),

new CssExtractRspackPlugin({
filename: `../css/${appName}-[name].css?v=[contenthash]`,
chunkFilename: `../css/${appName}-[name].css?v=[contenthash]`,
ignoreOrder: true,
}),
],

resolve: {
extensions: ['*', '.ts', '.js', '.vue', '.json'],
symlinks: false,
extensionAlias: {
'.js': ['.js', '.ts'],
},
alias: {
vue$: 'vue/dist/vue.runtime.esm.js',
'@': path.resolve(__dirname, 'src'),
},
fallback: {
fs: false,
},
},

cache: true,
}
}

export default defineConfig(config)
2 changes: 0 additions & 2 deletions src/editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@ import {
} from './components/Editor.provider.ts'
import { ACTION_ATTACHMENT_PROMPT } from './components/Editor/MediaHandler.provider.js'
import { openLink } from './helpers/links.js'
// eslint-disable-next-line import/no-unresolved, n/no-missing-import
import 'vite/modulepreload-polyfill'

const apiVersion = '1.3'

Expand Down
Loading
Loading