Skip to content

Commit 27e2ce0

Browse files
alexgrozavivov
andauthored
feat: migrate editor-ui to Vite.js and various DX improvements (N8N-2277) (#4061)
* feat: Added vite.js dependencies. * chore: Removed tests folder to follow same structure as design-system * chore: Removed unused testing config. * chore: Created vite.js index.html * refactor: Updated scss structure and imports. * refactor: Updated workflow building. * fix: Cleared up all workflow dependency cycles. Added proper package.json imports config. * feat: Got a working build using Vite. Need to fix issues next. * fix: Progress! Getting process.env error. * fix: Changed process.env to import.meta.env. * fix: Fixed circular imports that used require(). Fixed monaco editor. * chore: Removed commented code. * chore: Cleaned up package.json * feat: Made necessary changes to replace base path in css files. * feat: Serve CSS files for `editor-ui` Vite migration (#4069) :zap: Serve CSS files for Vite migration * chore: Fixed package-lock.json. * fix: Fixed build after centralized tsconfig update. * fix: Removed lodash-es replacement. * fix: Commented out vitest test command. * style: Fixed linting issues. * fix: Added lodash-es hotfix back. * chore: Updated package-lock.json * refactor: Renamed all n8n scss variables to no longer be defined as private. * feat(editor): add application-wide el-button replacement. * fix(editor): Fix import in page alert after merge. * chore(editor): update package-lock.json. * fix: Case sensitive lodash-es replacement for vue-agile. * fix: add alias for lodash-es camelcase import. * fix: add patch-package support for fixing quill * feat: add patch-package on postinstall * fix: update quill patch path. * refactor: rename quill patch * fix: update quill version. * fix: update quill patch * fix: fix linting rules after installing eslint in design-system * fix: update date picker button to have primary color * test: update callout component snapshots * fix(editor): fix linting issues in editor after enabling eslint * fix(cli): add /assets/* to auth ignore endpoints in server * chore: update package-lock.json * chore: update package-lock.json * fix(editor): fix linting issues * feat: add vite-legacy support * fix: update workflow package interface imports to type imports. * chore: update package-lock.json * fix(editor) fix importing translations other than english * fix(editor): remove test command until vitest is added * fix: increase memory allocation for vite build * fix: add patch-package patches to n8n-custom docker build * fix: add performance and load time improvements * fix: add proper typing to setNodeType * chore: update package-lock.json * style: use generic type for reduce in setNodeType Co-authored-by: Iván Ovejero <[email protected]>
1 parent e709cb5 commit 27e2ce0

File tree

248 files changed

+6208
-10833
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

248 files changed

+6208
-10833
lines changed

docker/images/n8n-custom/Dockerfile

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ RUN \
99

1010
COPY turbo.json package.json package-lock.json tsconfig.json ./
1111
COPY packages ./packages
12+
COPY patches ./patches
1213

1314
RUN chown -R node:node .
1415
RUN npm config set legacy-peer-deps true

package-lock.json

+5,457-10,217
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+2
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
"lint": "turbo run lint",
1212
"lintfix": "turbo run lintfix",
1313
"optimize-svg": "find ./packages -name '*.svg' ! -name 'pipedrive.svg' -print0 | xargs -0 -P16 -L20 npx svgo",
14+
"postinstall": "patch-package",
1415
"start": "run-script-os",
1516
"start:default": "cd packages/cli/bin && ./n8n",
1617
"start:tunnel": "./packages/cli/bin/n8n start --tunnel",
@@ -21,6 +22,7 @@
2122
"worker": "./packages/cli/bin/n8n worker"
2223
},
2324
"devDependencies": {
25+
"patch-package": "^6.4.7",
2426
"rimraf": "^3.0.2",
2527
"run-script-os": "^1.0.7",
2628
"turbo": "1.2.15"

packages/cli/src/Server.ts

+28-1
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ import {
147147
WebhookServer,
148148
WorkflowExecuteAdditionalData,
149149
} from '.';
150+
import glob from 'fast-glob';
150151
import { ResponseError } from './ResponseHelper';
151152

152153
require('body-parser-xml')(bodyParser);
@@ -389,6 +390,7 @@ class App {
389390
const excludeEndpoints = config.getEnv('security.excludeEndpoints');
390391

391392
const ignoredEndpoints = [
393+
'assets',
392394
'healthz',
393395
'metrics',
394396
this.endpointWebhook,
@@ -1753,11 +1755,28 @@ class App {
17531755
const editorUiPath = require.resolve('n8n-editor-ui');
17541756
const filePath = pathJoin(pathDirname(editorUiPath), 'dist', 'index.html');
17551757
const n8nPath = config.getEnv('path');
1758+
const basePathRegEx = /\/%BASE_PATH%\//g;
17561759

17571760
let readIndexFile = readFileSync(filePath, 'utf8');
1758-
readIndexFile = readIndexFile.replace(/\/%BASE_PATH%\//g, n8nPath);
1761+
readIndexFile = readIndexFile.replace(basePathRegEx, n8nPath);
17591762
readIndexFile = readIndexFile.replace(/\/favicon.ico/g, `${n8nPath}favicon.ico`);
17601763

1764+
const cssPath = pathJoin(pathDirname(editorUiPath), 'dist', '**/*.css');
1765+
const cssFiles: Record<string, string> = {};
1766+
glob.sync(cssPath).forEach((filePath) => {
1767+
let readFile = readFileSync(filePath, 'utf8');
1768+
readFile = readFile.replace(basePathRegEx, n8nPath);
1769+
cssFiles[filePath.replace(pathJoin(pathDirname(editorUiPath), 'dist'), '')] = readFile;
1770+
});
1771+
1772+
const jsPath = pathJoin(pathDirname(editorUiPath), 'dist', '**/*.js');
1773+
const jsFiles: Record<string, string> = {};
1774+
glob.sync(jsPath).forEach((filePath) => {
1775+
let readFile = readFileSync(filePath, 'utf8');
1776+
readFile = readFile.replace(basePathRegEx, n8nPath);
1777+
jsFiles[filePath.replace(pathJoin(pathDirname(editorUiPath), 'dist'), '')] = readFile;
1778+
});
1779+
17611780
const hooksUrls = config.getEnv('externalFrontendHooksUrls');
17621781

17631782
let scriptsString = '';
@@ -1793,6 +1812,14 @@ class App {
17931812
res.send(readIndexFile);
17941813
});
17951814

1815+
this.app.get('/assets/*.css', async (req: express.Request, res: express.Response) => {
1816+
res.type('text/css').send(cssFiles[req.url]);
1817+
});
1818+
1819+
this.app.get('/assets/*.js', async (req: express.Request, res: express.Response) => {
1820+
res.type('text/javascript').send(jsFiles[req.url]);
1821+
});
1822+
17961823
// Serve the website
17971824
this.app.use(
17981825
'/',

packages/design-system/gulpfile.js

-36
This file was deleted.

packages/design-system/package.json

+5-10
Original file line numberDiff line numberDiff line change
@@ -13,19 +13,15 @@
1313
"url": "git+https://github.com/n8n-io/n8n.git"
1414
},
1515
"scripts": {
16-
"build": "npm run build:theme",
17-
"build:vue": "vite build",
16+
"build": "vite build",
1817
"build:vue:typecheck": "vue-tsc --emitDeclarationOnly",
19-
"dev": "npm run watch:theme",
2018
"test": "vitest run",
2119
"test:ci": "vitest run --coverage",
2220
"test:dev": "vitest",
2321
"build:storybook": "build-storybook",
2422
"storybook": "start-storybook -p 6006",
2523
"lint": "tslint -p tsconfig.json -c tslint.json && eslint .",
26-
"lintfix": "tslint --fix -p tsconfig.json -c tslint.json && eslint . --fix",
27-
"build:theme": "gulp build:theme",
28-
"watch:theme": "gulp watch:theme"
24+
"lintfix": "tslint --fix -p tsconfig.json -c tslint.json && eslint . --fix"
2925
},
3026
"peerDependencies": {
3127
"@fortawesome/fontawesome-svg-core": "1.x",
@@ -54,10 +50,9 @@
5450
"babel-loader": "^8.2.2",
5551
"c8": "7.11.0",
5652
"core-js": "^3.6.5",
57-
"gulp": "^4.0.0",
58-
"gulp-autoprefixer": "^4.0.0",
59-
"gulp-clean-css": "^4.3.0",
60-
"gulp-dart-sass": "^1.0.2",
53+
"eslint": "^8.0.0",
54+
"eslint-plugin-prettier": "^3.4.0",
55+
"eslint-plugin-vue": "^7.16.0",
6156
"jsdom": "19.0.0",
6257
"markdown-it": "^12.3.2",
6358
"markdown-it-emoji": "^2.0.0",

packages/design-system/src/components/N8nActionToggle/ActionToggle.vue

+3-3
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,9 @@ import Vue from 'vue';
4747
export default Vue.extend({
4848
name: 'n8n-action-toggle',
4949
components: {
50-
ElDropdown,
51-
ElDropdownMenu,
52-
ElDropdownItem,
50+
ElDropdown, // eslint-disable-line @typescript-eslint/no-unsafe-assignment
51+
ElDropdownMenu, // eslint-disable-line @typescript-eslint/no-unsafe-assignment
52+
ElDropdownItem, // eslint-disable-line @typescript-eslint/no-unsafe-assignment
5353
N8nIcon,
5454
},
5555
props: {

packages/design-system/src/components/N8nAvatar/Avatar.vue

+1-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ export default Vue.extend({
4545
},
4646
},
4747
components: {
48-
Avatar,
48+
Avatar, // eslint-disable-line @typescript-eslint/no-unsafe-assignment
4949
},
5050
computed: {
5151
initials() {

packages/design-system/src/components/N8nButton/Button.vue

+11-11
Original file line numberDiff line numberDiff line change
@@ -96,25 +96,25 @@ export default Vue.extend({
9696
return this.disabled ? 'true' : 'false';
9797
},
9898
classes(): string {
99-
return `button ${this.$style['button']} ${this.$style[this.type]}` +
99+
return `button ${this.$style.button} ${this.$style[this.type]}` +
100100
`${this.size ? ` ${this.$style[this.size]}` : ''}` +
101-
`${this.outline ? ` ${this.$style['outline']}` : ''}` +
102-
`${this.loading ? ` ${this.$style['loading']}` : ''}` +
101+
`${this.outline ? ` ${this.$style.outline}` : ''}` +
102+
`${this.loading ? ` ${this.$style.loading}` : ''}` +
103103
`${this.float ? ` ${this.$style[`float-${this.float}`]}` : ''}` +
104-
`${this.text ? ` ${this.$style['text']}` : ''}` +
105-
`${this.disabled ? ` ${this.$style['disabled']}` : ''}` +
106-
`${this.block ? ` ${this.$style['block']}` : ''}` +
107-
`${this.active ? ` ${this.$style['active']}` : ''}` +
108-
`${this.icon || this.loading ? ` ${this.$style['icon']}` : ''}` +
109-
`${this.square ? ` ${this.$style['square']}` : ''}`;
104+
`${this.text ? ` ${this.$style.text}` : ''}` +
105+
`${this.disabled ? ` ${this.$style.disabled}` : ''}` +
106+
`${this.block ? ` ${this.$style.block}` : ''}` +
107+
`${this.active ? ` ${this.$style.active}` : ''}` +
108+
`${this.icon || this.loading ? ` ${this.$style.icon}` : ''}` +
109+
`${this.square ? ` ${this.$style.square}` : ''}`;
110110
},
111111
},
112112
});
113113
</script>
114114

115115
<style lang="scss" module>
116-
@import '../../../theme/src/mixins/utils';
117-
@import '../../../theme/src/common/var';
116+
@import '../../css/mixins/utils';
117+
@import '../../css/common/var';
118118
119119
.button {
120120
display: inline-block;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import ElButton from "./ElButton.vue";
2+
3+
export default ElButton;

packages/design-system/src/components/N8nCallout/Callout.vue

+3-5
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
<script lang="ts">
2020
import Vue from 'vue';
2121
import N8nIcon from '../N8nIcon';
22-
import N8nText from '../N8nText';
2322
2423
const CALLOUT_DEFAULT_ICONS: { [key: string]: string } = {
2524
info: 'info-circle',
@@ -32,7 +31,6 @@ export default Vue.extend({
3231
name: 'n8n-callout',
3332
components: {
3433
N8nIcon,
35-
N8nText,
3634
},
3735
props: {
3836
theme: {
@@ -43,14 +41,14 @@ export default Vue.extend({
4341
},
4442
icon: {
4543
type: String,
46-
default: 'info-circle'
44+
default: 'info-circle',
4745
},
4846
},
4947
computed: {
5048
classes(): string[] {
5149
return [
5250
'n8n-callout',
53-
this.$style['callout'],
51+
this.$style.callout,
5452
this.$style[this.theme],
5553
];
5654
},
@@ -61,7 +59,7 @@ export default Vue.extend({
6159
6260
return this.icon;
6361
},
64-
}
62+
},
6563
});
6664
</script>
6765

packages/design-system/src/components/N8nCallout/__tests__/__snapshots__/Callout.spec.ts.snap

+7-7
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ exports[`components > N8nCallout > should render additional slots correctly 1`]
66
<div class=\\"_icon_p74de_40\\">
77
<n8n-icon-stub icon=\\"code-branch\\" size=\\"large\\"></n8n-icon-stub>
88
</div>
9-
<n8n-text-stub size=\\"small\\" tag=\\"span\\">This is a secondary callout.</n8n-text-stub>&nbsp; <n8n-link-stub size=\\"small\\">Do something!</n8n-link-stub>
9+
<n8n-text-stub size=\\"small\\">This is a secondary callout.</n8n-text-stub>&nbsp; <n8n-link-stub size=\\"small\\">Do something!</n8n-link-stub>
1010
</div>
1111
<n8n-link-stub theme=\\"secondary\\" size=\\"small\\" bold=\\"true\\" underline=\\"true\\" to=\\"https://n8n.io\\">Learn more</n8n-link-stub>
1212
</div>"
@@ -18,7 +18,7 @@ exports[`components > N8nCallout > should render custom theme correctly 1`] = `
1818
<div class=\\"_icon_p74de_40\\">
1919
<n8n-icon-stub icon=\\"code-branch\\" size=\\"large\\"></n8n-icon-stub>
2020
</div>
21-
<n8n-text-stub size=\\"small\\" tag=\\"span\\">This is a secondary callout.</n8n-text-stub>&nbsp;
21+
<n8n-text-stub size=\\"small\\">This is a secondary callout.</n8n-text-stub>&nbsp;
2222
</div>
2323
</div>"
2424
`;
@@ -29,7 +29,7 @@ exports[`components > N8nCallout > should render danger theme correctly 1`] = `
2929
<div class=\\"_icon_p74de_40\\">
3030
<n8n-icon-stub icon=\\"times-circle\\" size=\\"large\\"></n8n-icon-stub>
3131
</div>
32-
<n8n-text-stub size=\\"small\\" tag=\\"span\\">This is a danger callout.</n8n-text-stub>&nbsp;
32+
<n8n-text-stub size=\\"small\\">This is a danger callout.</n8n-text-stub>&nbsp;
3333
</div>
3434
</div>"
3535
`;
@@ -40,7 +40,7 @@ exports[`components > N8nCallout > should render info theme correctly 1`] = `
4040
<div class=\\"_icon_p74de_40\\">
4141
<n8n-icon-stub icon=\\"info-circle\\" size=\\"large\\"></n8n-icon-stub>
4242
</div>
43-
<n8n-text-stub size=\\"small\\" tag=\\"span\\">This is an info callout.</n8n-text-stub>&nbsp;
43+
<n8n-text-stub size=\\"small\\">This is an info callout.</n8n-text-stub>&nbsp;
4444
</div>
4545
</div>"
4646
`;
@@ -51,7 +51,7 @@ exports[`components > N8nCallout > should render secondary theme correctly 1`] =
5151
<div class=\\"_icon_p74de_40\\">
5252
<n8n-icon-stub icon=\\"info-circle\\" size=\\"medium\\"></n8n-icon-stub>
5353
</div>
54-
<n8n-text-stub size=\\"small\\" tag=\\"span\\">This is a secondary callout.</n8n-text-stub>&nbsp;
54+
<n8n-text-stub size=\\"small\\">This is a secondary callout.</n8n-text-stub>&nbsp;
5555
</div>
5656
</div>"
5757
`;
@@ -62,7 +62,7 @@ exports[`components > N8nCallout > should render success theme correctly 1`] = `
6262
<div class=\\"_icon_p74de_40\\">
6363
<n8n-icon-stub icon=\\"check-circle\\" size=\\"large\\"></n8n-icon-stub>
6464
</div>
65-
<n8n-text-stub size=\\"small\\" tag=\\"span\\">This is a success callout.</n8n-text-stub>&nbsp;
65+
<n8n-text-stub size=\\"small\\">This is a success callout.</n8n-text-stub>&nbsp;
6666
</div>
6767
</div>"
6868
`;
@@ -73,7 +73,7 @@ exports[`components > N8nCallout > should render warning theme correctly 1`] = `
7373
<div class=\\"_icon_p74de_40\\">
7474
<n8n-icon-stub icon=\\"exclamation-triangle\\" size=\\"large\\"></n8n-icon-stub>
7575
</div>
76-
<n8n-text-stub size=\\"small\\" tag=\\"span\\">This is a warning callout.</n8n-text-stub>&nbsp;
76+
<n8n-text-stub size=\\"small\\">This is a warning callout.</n8n-text-stub>&nbsp;
7777
</div>
7878
</div>"
7979
`;

packages/design-system/src/components/N8nCheckbox/Checkbox.vue

+2-2
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import N8nInputLabel from '../N8nInputLabel';
2424
export default Vue.extend({
2525
name: 'n8n-checkbox',
2626
components: {
27-
ElCheckbox,
27+
ElCheckbox, // eslint-disable-line @typescript-eslint/no-unsafe-assignment
2828
N8nInputLabel,
2929
},
3030
props: {
@@ -59,7 +59,7 @@ export default Vue.extend({
5959
onChange(event: Event) {
6060
this.$emit("input", event);
6161
},
62-
}
62+
},
6363
});
6464
</script>
6565

packages/design-system/src/components/N8nFormInput/FormInput.vue

+1-1
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ export default mixins(Locale).extend({
179179
},
180180
methods: {
181181
getValidationError(): ReturnType<IValidator['validate']> {
182-
const rules = (this.validationRules || []) as (Rule | RuleGroup)[];
182+
const rules = (this.validationRules || []) as Array<Rule | RuleGroup>;
183183
const validators = {
184184
...VALIDATORS,
185185
...(this.validators || {}),

packages/design-system/src/components/N8nFormInputs/FormInputs.vue

+6-6
Original file line numberDiff line numberDiff line change
@@ -67,19 +67,19 @@ export default Vue.extend({
6767
});
6868
6969
if (this.eventBus) {
70-
this.eventBus.$on('submit', this.onSubmit);
70+
this.eventBus.$on('submit', this.onSubmit); // eslint-disable-line @typescript-eslint/unbound-method
7171
}
7272
},
7373
computed: {
7474
filteredInputs(): IFormInput[] {
7575
return (this.inputs as IFormInput[]).filter(
7676
(input) => typeof input.shouldDisplay === 'function'
7777
? input.shouldDisplay(this.values)
78-
: true
78+
: true,
7979
);
8080
},
8181
isReadyToSubmit(): boolean {
82-
for (let key in this.validity) {
82+
for (const key in this.validity) {
8383
if (!this.validity[key]) {
8484
return false;
8585
}
@@ -92,17 +92,17 @@ export default Vue.extend({
9292
onInput(name: string, value: any) {
9393
this.values = {
9494
...this.values,
95-
[name]: value,
95+
[name]: value, // eslint-disable-line @typescript-eslint/no-unsafe-assignment
9696
};
97-
this.$emit('input', {name, value});
97+
this.$emit('input', {name, value}); // eslint-disable-line @typescript-eslint/no-unsafe-assignment
9898
},
9999
onValidate(name: string, valid: boolean) {
100100
Vue.set(this.validity, name, valid);
101101
},
102102
onSubmit() {
103103
this.showValidationWarnings = true;
104104
if (this.isReadyToSubmit) {
105-
const toSubmit = (this.filteredInputs as IFormInput[]).reduce<{ [key: string]: unknown }>((accu, input) => {
105+
const toSubmit = (this.filteredInputs ).reduce<{ [key: string]: unknown }>((accu, input) => {
106106
if (this.values[input.name]) {
107107
accu[input.name] = this.values[input.name];
108108
}

0 commit comments

Comments
 (0)