diff --git a/.eslintignore b/.eslintignore index a6d7a991e62..904b3531dfd 100644 --- a/.eslintignore +++ b/.eslintignore @@ -8,4 +8,5 @@ **/*.d.ts **/playwright*/** **/vite.config.js +**/vite.prod.config.js **/node_modules diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index cc954f229b9..3295692653e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -310,3 +310,85 @@ jobs: name: Test Results path: test-results/ retention-days: 7 + + e2e-prod: + runs-on: macos-latest + strategy: + matrix: + node-version: [16.8] + browser: ['chromium'] + editor-mode: ['rich-text', 'plain-text'] + events-mode: ['modern-events'] + env: + CI: true + E2E_EDITOR_MODE: ${{ matrix.editor-mode }} + E2E_EVENTS_MODE: ${{ matrix.events-mode }} + steps: + - uses: actions/checkout@v3 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node-version }} + - run: npm i -g npm@8 + - uses: actions/cache@v3 + id: cache + with: + path: | + node_modules + packages/playwright-core/node_modules + ~/Library/Caches/ms-playwright + key: ${{ runner.os }}-v${{ secrets.CACHE_VERSION }}-${{ hashFiles('package-lock.json') }} + - name: Install dependencies + if: steps.cache.outputs.cache-hit != 'true' + run: npm ci + - name: Download browsers + run: npx playwright install + - run: npm run test-e2e-prod-ci:${{ matrix.browser }} + - name: Upload Artifacts + if: failure() + uses: actions/upload-artifact@v3 + with: + name: Test Results + path: test-results/ + retention-days: 7 + + e2e-collab-prod: + runs-on: macos-latest + strategy: + matrix: + node-version: [16.8] + browser: ['chromium'] + editor-mode: ['rich-text'] + events-mode: ['modern-events'] + env: + CI: true + E2E_EDITOR_MODE: ${{ matrix.editor-mode }} + E2E_EVENTS_MODE: ${{ matrix.events-mode }} + steps: + - uses: actions/checkout@v3 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node-version }} + - run: npm i -g npm@8 + - uses: actions/cache@v3 + id: cache + with: + path: | + node_modules + packages/playwright-core/node_modules + ~/Library/Caches/ms-playwright + key: ${{ runner.os }}-v${{ secrets.CACHE_VERSION }}-${{ hashFiles('package-lock.json') }} + - name: Install dependencies + if: steps.cache.outputs.cache-hit != 'true' + run: npm ci + - name: Download browsers + run: npx playwright install + - run: npm run test-e2e-collab-prod-ci:${{ matrix.browser }} + - name: Upload Artifacts + if: failure() + uses: actions/upload-artifact@v3 + with: + name: Test Results + path: test-results/ + retention-days: 7 diff --git a/.prettierignore b/.prettierignore index 6a917f22b34..dac34b8b688 100644 --- a/.prettierignore +++ b/.prettierignore @@ -7,6 +7,7 @@ packages/**/config/*.js packages/playwright packages/playwright-core packages/**/vite.config.js +packages/**/vite.prod.config.js **/*.md **/node_modules flow-typed diff --git a/package.json b/package.json index 390c5a0ea6f..e049f4853ac 100644 --- a/package.json +++ b/package.json @@ -15,9 +15,11 @@ "scripts": { "start": "cross-env NODE_ENV=development concurrently \"npm:collab\" \"npm run dev --prefix packages/lexical-playground\"", "start:website": "npm run start --prefix packages/lexical-website-new -- --port 3001", + "start:playground": "serve packages/lexical-playground/build -l 4000", "dev": "npm run dev --prefix packages/lexical-playground", "build": "node scripts/build.js", "build-prod": "npm run clean && npm run build -- --prod", + "build-playground-prod": "npm run build-prod && npm run build-prod --prefix packages/lexical-playground", "build-release": "npm run build-prod -- --release", "build-www": "npm run clean && npm run build -- --www && npm run build -- --www --prod && npm run prepare-www", "clean": "node scripts/clean.js", @@ -40,15 +42,20 @@ "test-e2e-collab:chromium": "cross-env E2E_BROWSER=chromium E2E_EDITOR_MODE=rich-text-with-collab playwright test --project=\"chromium\"", "test-e2e-collab:firefox": "cross-env E2E_BROWSER=firefox E2E_EDITOR_MODE=rich-text-with-collab playwright test --project=\"firefox\"", "test-e2e-collab:webkit": "cross-env E2E_BROWSER=webkit E2E_EDITOR_MODE=rich-text-with-collab playwright test --project=\"webkit\"", + "test-e2e-prod:chromium": "cross-env E2E_BROWSER=chromium E2E_PORT=4000 playwright test --project=\"chromium\"", + "test-e2e-collab-prod:chromium": "cross-env E2E_BROWSER=chromium E2E_PORT=4000 E2E_EDITOR_MODE=rich-text-with-collab playwright test --project=\"chromium\"", "test-e2e-ci:chromium": "cross-env E2E_PORT=4000 start-server-and-test prepare-ci 4000 test-e2e:chromium", "test-e2e-ci:firefox": "cross-env E2E_PORT=4000 start-server-and-test prepare-ci 4000 test-e2e:firefox", "test-e2e-ci:webkit": "cross-env E2E_PORT=4000 start-server-and-test prepare-ci 4000 test-e2e:webkit", "test-e2e-collab-ci:chromium": "cross-env E2E_PORT=4000 start-server-and-test prepare-ci-collab 4000 test-e2e-collab:chromium", "test-e2e-collab-ci:firefox": "cross-env E2E_PORT=4000 start-server-and-test prepare-ci-collab 4000 test-e2e-collab:firefox", "test-e2e-collab-ci:webkit": "cross-env E2E_PORT=4000 start-server-and-test prepare-ci-collab 4000 test-e2e-collab:webkit", + "test-e2e-prod-ci:chromium": "cross-env E2E_PORT=4000 start-server-and-test prepare-ci-prod 4000 test-e2e-prod:chromium", + "test-e2e-collab-prod-ci:chromium": "cross-env E2E_PORT=4000 start-server-and-test prepare-ci-collab-prod 4000 test-e2e-collab-prod:chromium", "run-all": "npm-run-all debug-test-e2e:*", "debug-test-e2e": "cross-env playwright test --debug", "debug-test-e2e:chromium": "cross-env E2E_BROWSER=chromium playwright test --debug --project=\"chromium\"", + "debug-test-e2e-prod:chromium": "cross-env E2E_BROWSER=chromium E2E_PORT=4173 playwright test --debug --project=\"chromium\"", "debug-test-e2e:firefox": "cross-env E2E_BROWSER=firefox playwright test --debug --project=\"firefox\"", "debug-test-e2e:webkit": "cross-env E2E_BROWSER=webkit playwright test --debug --project=\"webkit\"", "debug-test-e2e-legacy": "cross-env playwright test --debug", @@ -76,8 +83,10 @@ "prettier": "prettier --list-different .", "ci-check": "npm-run-all --parallel tsc flow prettier lint", "prettier:fix": "prettier --write .", - "prepare-ci": "npm run build --prefix packages/lexical-playground && serve packages/lexical-playground/build -l 4000", - "prepare-ci-collab": "npm run build --prefix packages/lexical-playground && concurrently \"npm run collab\" \"serve packages/lexical-playground/build -l 4000\"", + "prepare-ci": "npm run build-dev --prefix packages/lexical-playground && serve packages/lexical-playground/build -l 4000", + "prepare-ci-collab": "npm run build-dev --prefix packages/lexical-playground && concurrently \"npm run collab\" \"serve packages/lexical-playground/build -l 4000\"", + "prepare-ci-prod": "npm run build-playground-prod && serve packages/lexical-playground/build -l 4000", + "prepare-ci-collab-prod": "npm run build-playground-prod && concurrently \"npm run collab\" \"serve packages/lexical-playground/build -l 4000\"", "prepare-release": "npm run build-release && node ./scripts/npm/prepare-release.js", "prepare": "husky install", "prepare-www": "node scripts/www/rewriteImports.js", diff --git a/packages/lexical-playground/__tests__/e2e/MaxLength.spec.mjs b/packages/lexical-playground/__tests__/e2e/MaxLength.spec.mjs index 586b3a498af..0fcb384811b 100644 --- a/packages/lexical-playground/__tests__/e2e/MaxLength.spec.mjs +++ b/packages/lexical-playground/__tests__/e2e/MaxLength.spec.mjs @@ -8,12 +8,9 @@ import { assertHTML, - assertSelection, - clearEditor, focusEditor, html, initialize, - pasteFromClipboard, test, } from '../utils/index.mjs'; @@ -24,17 +21,11 @@ test.describe('MaxLength', () => { ); test(`can restrict the text to specified length`, async ({page}) => { await focusEditor(page); + await page.keyboard.type( 'lorem ipsum dolor sit amet, consectetuer adipiscing elit', ); - await assertSelection(page, { - anchorOffset: 30, - anchorPath: [0, 0, 0], - focusOffset: 30, - focusPath: [0, 0, 0], - }); - await assertHTML( page, html` @@ -47,25 +38,8 @@ test.describe('MaxLength', () => { ); await page.keyboard.press('ArrowRight'); - await page.keyboard.type('Some more text'); - - await assertHTML( - page, - html` -

- lorem ipsum dolor sit amet, co -

- `, - ); - }); - test(`can restrict pasted text to specified length`, async ({page}) => { - await focusEditor(page); - await pasteFromClipboard(page, { - 'text/plain': 'lorem ipsum dolor sit amet, consectetuer adipiscing elit', - }); + await page.keyboard.type('Some more text'); await assertHTML( page, @@ -78,51 +52,4 @@ test.describe('MaxLength', () => { `, ); }); - - test(`can restrict emojis on boundaries`, async ({page}) => { - await pasteFromClipboard(page, { - 'text/plain': 'lorem ipsum dolor sit amet, consectetur adipiscing elit', - }); - await page.keyboard.press('Backspace'); - await page.keyboard.type('💏'); - - await assertHTML( - page, - html` -

- lorem ipsum dolor sit amet, c -

- `, - ); - - await page.keyboard.press('Backspace'); - await page.keyboard.type('💏'); - - await assertHTML( - page, - html` -

- lorem ipsum dolor sit amet, 💏 -

- `, - ); - - await clearEditor(page); - await page.keyboard.type('👨‍💻👨‍💻👨‍💻👨‍💻👨‍💻👨‍💻👨‍💻'); - - await assertHTML( - page, - html` -

- 👨‍💻👨‍💻👨‍💻👨‍💻👨‍💻👨‍💻 -

- `, - ); - }); }); diff --git a/packages/lexical-playground/package.json b/packages/lexical-playground/package.json index 2e9e24ac550..355c56a44ef 100644 --- a/packages/lexical-playground/package.json +++ b/packages/lexical-playground/package.json @@ -4,7 +4,9 @@ "private": true, "scripts": { "dev": "vite --host", - "build": "vite build", + "build-dev": "vite build", + "build-prod": "vite build --config vite.prod.config.js", + "build-vercel": "(cd ../../ && node ./scripts/build.js) && npm run build-prod", "preview": "vite preview" }, "dependencies": { diff --git a/packages/lexical-playground/vite.prod.config.js b/packages/lexical-playground/vite.prod.config.js new file mode 100644 index 00000000000..fc468e361cc --- /dev/null +++ b/packages/lexical-playground/vite.prod.config.js @@ -0,0 +1,183 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + */ + +import {defineConfig} from 'vite'; +import react from '@vitejs/plugin-react'; +import {resolve} from 'path'; +import path from 'path'; +import fs from 'fs'; +import {replaceCodePlugin} from 'vite-plugin-replace'; +import babel from '@rollup/plugin-babel'; + +const moduleResolution = [ + { + find: /lexical$/, + replacement: path.resolve('../lexical/dist/Lexical.js'), + }, + { + find: '@lexical/clipboard', + replacement: path.resolve('../lexical-clipboard/dist/LexicalClipboard.js'), + }, + { + find: '@lexical/selection', + replacement: path.resolve('../lexical-selection/dist/LexicalSelection.js'), + }, + { + find: '@lexical/text', + replacement: path.resolve('../lexical-text/dist/LexicalText.js'), + }, + { + find: '@lexical/headless', + replacement: path.resolve('../lexical-headless/dist/LexicalHeadless.js'), + }, + { + find: '@lexical/html', + replacement: path.resolve('../lexical-html/dist/LexicalHtml.js'), + }, + { + find: '@lexical/hashtag', + replacement: path.resolve('../lexical-hashtag/dist/LexicalHashtag.js'), + }, + { + find: '@lexical/history', + replacement: path.resolve('../lexical-history/dist/LexicalHistory.js'), + }, + { + find: '@lexical/list', + replacement: path.resolve('../lexical-list/dist/LexicalList.js'), + }, + { + find: '@lexical/file', + replacement: path.resolve('../lexical-file/dist/LexicalFile.js'), + }, + { + find: '@lexical/table', + replacement: path.resolve('../lexical-table/dist/LexicalTable.js'), + }, + { + find: '@lexical/offset', + replacement: path.resolve('../lexical-offset/dist/LexicalOffset.js'), + }, + { + find: '@lexical/utils', + replacement: path.resolve('../lexical-utils/dist/LexicalUtils.js'), + }, + { + find: '@lexical/code', + replacement: path.resolve('../lexical-code/dist/LexicalCode.js'), + }, + { + find: '@lexical/plain-text', + replacement: path.resolve('../lexical-plain-text/dist/LexicalPlainText.js'), + }, + { + find: '@lexical/rich-text', + replacement: path.resolve('../lexical-rich-text/dist/LexicalRichText.js'), + }, + { + find: '@lexical/dragon', + replacement: path.resolve('../lexical-dragon/dist/LexicalDragon.js'), + }, + { + find: '@lexical/link', + replacement: path.resolve('../lexical-link/dist/LexicalLink.js'), + }, + { + find: '@lexical/overflow', + replacement: path.resolve('../lexical-overflow/dist/LexicalOverflow.js'), + }, + { + find: '@lexical/markdown', + replacement: path.resolve('../lexical-markdown/dist/LexicalMarkdown.js'), + }, + { + find: '@lexical/mark', + replacement: path.resolve('../lexical-mark/dist/LexicalMark.js'), + }, + { + find: '@lexical/yjs', + replacement: path.resolve('../lexical-yjs/dist/LexicalYjs.js'), + }, + { + find: 'shared', + replacement: path.resolve('../shared/src'), + }, +]; +// Lexical React +[ + 'LexicalTreeView', + 'LexicalComposer', + 'LexicalComposerContext', + 'useLexicalIsTextContentEmpty', + 'useLexicalTextEntity', + 'LexicalContentEditable', + 'LexicalNestedComposer', + 'LexicalHorizontalRuleNode', + 'LexicalDecoratorBlockNode', + 'LexicalBlockWithAlignableContents', + 'useLexicalNodeSelection', + 'LexicalMarkdownShortcutPlugin', + 'LexicalCharacterLimitPlugin', + 'LexicalHashtagPlugin', + 'LexicalPlainTextPlugin', + 'LexicalRichTextPlugin', + 'LexicalClearEditorPlugin', + 'LexicalCollaborationPlugin', + 'LexicalHistoryPlugin', + 'LexicalTablePlugin', + 'LexicalLinkPlugin', + 'LexicalListPlugin', + 'LexicalCheckListPlugin', + 'LexicalAutoFocusPlugin', + 'LexicalAutoLinkPlugin', + 'LexicalOnChangePlugin', + 'LexicalAutoScrollPlugin', +].forEach((module) => { + let resolvedPath = path.resolve(`../lexical-react/dist/${module}.js`); + moduleResolution.push({ + find: `@lexical/react/${module}`, + replacement: resolvedPath, + }); +}); + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [ + replaceCodePlugin({ + replacements: [ + { + from: /__DEV__/g, + to: 'true', + }, + ], + }), + babel({ + babelHelpers: 'bundled', + babelrc: false, + configFile: false, + exclude: '/**/node_modules/**', + extensions: ['jsx', 'js', 'ts', 'tsx', 'mjs'], + plugins: ['@babel/plugin-transform-flow-strip-types'], + presets: ['@babel/preset-react'], + }), + react(), + ], + resolve: { + alias: moduleResolution, + }, + build: { + outDir: 'build', + rollupOptions: { + input: { + main: new URL('./index.html', import.meta.url).pathname, + split: new URL('./split/index.html', import.meta.url).pathname, + }, + }, + commonjsOptions: {include: []}, + }, +});