ckeditor / ckeditor5-vue

Official CKEditor 5 Vue.js component.
https://ckeditor.com/ckeditor-5
Other
360 stars 77 forks source link

SSR fails #71

Closed avagru closed 4 years ago

avagru commented 5 years ago

I tried ssr rendering via npm run build but I fails. My project was a custom ckeditor building from source. Below are error logs.

Application is running on http://localhost:8089
error during render : /projects/OidiBDAGosxZiX6x3KSs/notebook/s6P7Shghqj0KaGmenZPe
..../node_modules/@ckeditor/ckeditor5-editor-classic/src/classiceditor.js:10
import Editor from '@ckeditor/ckeditor5-core/src/editor/editor';
       ^^^^^^
SyntaxError: Unexpected identifier
    at new Script (vm.js:74:7)
    at createScript (vm.js:246:10)
    at Object.runInThisContext (vm.js:298:10)
    at Module._compile (internal/modules/cjs/loader.js:657:28)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:700:10)
    at Module.load (internal/modules/cjs/loader.js:599:32)
    at tryModuleLoad (internal/modules/cjs/loader.js:538:12)
    at Function.Module._load (internal/modules/cjs/loader.js:530:3)
    at Module.require (internal/modules/cjs/loader.js:637:17)
    at require (internal/modules/cjs/helpers.js:20:18)
Mgsy commented 5 years ago

Hello, can you provide exact steps to reproduce, so we'll be able to investigate the issue? Code samples also will be appreciated.

avagru commented 5 years ago
import {Component, Prop, Vue} from 'vue-property-decorator';
import FileList from "@/components/notes/FileList.vue";
import _ from 'lodash';
import Tags from "@/components/notes/Tags.vue";
import {Note} from "@/swagger";
import FileUpload from "vue-upload-component";

import ClassicEditor from '@ckeditor/ckeditor5-editor-classic/src/classiceditor';
import EssentialsPlugin from '@ckeditor/ckeditor5-essentials/src/essentials';
import BoldPlugin from '@ckeditor/ckeditor5-basic-styles/src/bold';
import HeadingPlugin from '@ckeditor/ckeditor5-heading/src/heading';
import ItalicPlugin from '@ckeditor/ckeditor5-basic-styles/src/italic';
import LinkPlugin from '@ckeditor/ckeditor5-link/src/link';
import ParagraphPlugin from '@ckeditor/ckeditor5-paragraph/src/paragraph';
import BlockquotePlugin from '@ckeditor/ckeditor5-block-quote/src/blockquote';    
import TablePlugin from '@ckeditor/ckeditor5-table/src/table';
import TableToolbar from '@ckeditor/ckeditor5-table/src/tabletoolbar';
import MediaEmbed from '@ckeditor/ckeditor5-media-embed/src/mediaembed';

import CKFinder from '../../utils/ckeditor5-ckfinder/src/ckfinder';
import Image from '@ckeditor/ckeditor5-image/src/image';
import ImageUpload from '@ckeditor/ckeditor5-image/src/imageupload';
import ImageToolbar from '@ckeditor/ckeditor5-image/src/imagetoolbar';
import ImageCaption from '@ckeditor/ckeditor5-image/src/imagecaption';
import ImageStyle from '@ckeditor/ckeditor5-image/src/imagestyle';    

@Component({
    components: {Tags, FileList}
})
export default class NoteEditor extends Vue {

    @Prop({})
    private noteIndex: number;
    private isLoading: boolean = false;
    private noteCopy: Note = null;
    private editMode = false;

    private spinner = false;
    showDropTarget = false;
    private editor = null;
    private editorConfig = {
        plugins: [ 
            EssentialsPlugin,
            BoldPlugin,
            HeadingPlugin,
            ItalicPlugin,
            LinkPlugin,
            ParagraphPlugin,
            BlockquotePlugin,
            TablePlugin,
            TableToolbar,
            MediaEmbed,
            CKFinder,
            Image, 
            ImageUpload, 
            ImageToolbar, 
            ImageCaption, 
            ImageStyle 
        ],
        toolbar: {
            items: [
                'heading', 
                '|',
                'bold',
                'italic',
                'link',
                'bulletedList',
                'numberedList', 
                'imageUpload',
                'blockQuote',
                'insertTable',
                'mediaEmbed',
                'undo',
                'redo',
            ]
        },
        heading: {
            options: [
                { model: 'paragraph', title: 'Paragraph', class: 'ck-heading_paragraph' },
                { model: 'heading1', view: 'h1', title: 'Heading 1', class: 'ck-heading_heading1' },
                { model: 'heading2', view: 'h2', title: 'Heading 2', class: 'ck-heading_heading2' },
                { model: 'heading3', view: 'h3', title: 'Heading 3', class: 'ck-heading_heading3' }
            ]
        },
        table: {
            contentToolbar: [ 'tableColumn', 'tableRow', 'mergeTableCells' ]
        },
        ckfinder: {
            uploadUrl: 'https://content.dropboxapi.com/2/files/upload',
            headerToken: {},
            notebook: "",
            noteNumber: 0,
            apiPath: "",
        },
        image: {
            toolbar: [ 'imageTextAlternative', '|', 'ImageStyle:full', 'ImageStyle:side' ]
        }
    };
    private componentLoader;

    async created() {
        this.editor = ClassicEditor;
        // this.editor = require("@ckeditor/ckeditor5-build-classic");
        if (!this.note.id) {
            this.editMode = true; // no need to take a copy
        }
        let userInfo = await this.$firebase.auth()
        let user: firebase.User = userInfo.currentUser;
        this.editorConfig.ckfinder.headerToken = await user.getIdTokenResult();
        this.editorConfig.ckfinder.uploadUrl = "https://content.dropboxapi.com/2/files/upload";
        this.editorConfig.ckfinder.notebook = this.$route.params['notebook'];
        this.editorConfig.ckfinder.noteNumber = this.note.number;
        this.editorConfig.ckfinder.apiPath =  this.$apiPath;
    }

.....

avagru commented 5 years ago

Hello, Thank you for your reply.. This is my latest trying.

jodator commented 5 years ago

@star0422 are you trying to render the editor on server and return it to the browser? As such I think that this will not work as the editor must be instantiated on the browser. If it is not a case please explain more clearly what you try to achieve? What framework are you using. Also some details on environment setup (node version, browser, etc) might be also helpful.

oleq commented 5 years ago

Could be a duplicate of https://github.com/ckeditor/ckeditor5/issues/1511.

Also, what does the webpack config look like? Are the CKE5 dependencies transpiled?

avagru commented 5 years ago

I am trying to render the editor on server. In webpack, "npm run dev" run well. But "npm start" still fails. Node and npm are latest version. The web app is based on Vuejs. So I tried the vue app with builder from source.

avagru commented 5 years ago

This is package.json { "scripts": { "dev": "webpack-serve --config=./build/webpack.dev.js", "build": "webpack --config=./build/webpack.prod.js --hide-modules --progress", "start": "node server" }, "dependencies": { "@ckeditor/ckeditor5-autoformat": "^11.0.1", "@ckeditor/ckeditor5-basic-styles": "^11.1.0", "@ckeditor/ckeditor5-block-quote": "^11.0.1", "@ckeditor/ckeditor5-build-classic": "^11.2.0", "@ckeditor/ckeditor5-ckfinder": "^11.0.1", "@ckeditor/ckeditor5-dev-utils": "^12.0.1", "@ckeditor/ckeditor5-dev-webpack-plugin": "^8.0.1", "@ckeditor/ckeditor5-editor-classic": "^12.1.0", "@ckeditor/ckeditor5-essentials": "^11.0.1", "@ckeditor/ckeditor5-heading": "^11.0.1", "@ckeditor/ckeditor5-image": "^13.0.1", "@ckeditor/ckeditor5-link": "^11.0.1", "@ckeditor/ckeditor5-list": "^12.0.1", "@ckeditor/ckeditor5-media-embed": "^11.1.0", "@ckeditor/ckeditor5-paragraph": "^11.0.1", "@ckeditor/ckeditor5-table": "^12.0.1", "@ckeditor/ckeditor5-theme-lark": "^13.0.1", "@ckeditor/ckeditor5-vue": "^1.0.0-beta.1", "bootstrap-vue": "^2.0.0-rc.13", "express": "^4.16.3", "firebase": "^5.7.3", "install": "^0.12.2", "lodash": "^4.17.10", "lodash.clonedeep": "^4.5.0", "moment": "^2.24.0", "npm": "^6.9.0", "portable-fetch": "^3.0.0", "raw-loader": "^2.0.0", "vue": "^2.5.16", "vue-property-decorator": "^7.3.0", "vue-router": "^3.0.1", "vue-server-renderer": "^2.5.16", "vue-upload-component": "^2.8.19", "vuex": "^3.0.1", "vuex-router-sync": "^5.0.0" }, "devDependencies": { "@types/lodash.clonedeep": "^4.5.5", "@types/node": "^11.11.3", "autoprefixer": "^9.4.5", "connect-history-api-fallback": "^1.5.0", "css-loader": "^2.1.0", "file-loader": "^3.0.1", "glob-all": "^3.1.0", "html-webpack-plugin": "^3.2.0", "html-webpack-template": "^6.2.0", "koa-connect": "^2.0.1", "mini-css-extract-plugin": "^0.5.0", "node-sass": "^4.11.0", "optimize-css-assets-webpack-plugin": "^5.0.1", "postcss": "^7.0.13", "postcss-easy-import": "^3.0.0", "postcss-loader": "^3.0.0", "purgecss-webpack-plugin": "^1.4.0", "sass-loader": "^7.1.0", "style-loader": "^0.23.1", "ts-loader": "^5.3.3", "tsconfig-paths-webpack-plugin": "^3.0.4", "typescript": "^3.2.2", "vue-loader": "^15.0.10", "vue-template-compiler": "^2.5.16", "webpack": "^4.8.3", "webpack-cli": "^3.2.1", "webpack-merge": "^4.1.2", "webpack-node-externals": "^1.7.2", "webpack-serve": "^2.0.3", "webpackbar": "^3.1.5" } }

farin commented 5 years ago

Trying to use CKEditor-vue in Nuxt app with enabled SSR.

window is not defined

node_modules/@ckeditor/ckeditor5-vue/dist/ckeditor.js

internal/modules/cjs/loader.js:799:30
Module._compile
internal/modules/cjs/loader.js:810:10
Module._extensions..js
internal/modules/cjs/loader.js:666:32
Module.load
internal/modules/cjs/loader.js:606:12
tryModuleLoad
internal/modules/cjs/loader.js:598:3
Module._load
internal/modules/cjs/loader.js:705:19
Module.require
internal/modules/cjs/helpers.js:14:16
require
webpack:/external "@ckeditor/ckeditor5-vue":1:
Object.@ckeditor/ckeditor5-vue
webpack/bootstrap:25:
__webpack_require__
farin commented 5 years ago

unfortunatelly CKEditor module can't be even imported on server side, which makes impossible to make any workaroud except patching/rebuilding module itself

UPDATE: ok there is one easy workaround for Nuxt. Registering ckeditor component and importing all stuff just from plugin with ssr: false option (or registering there own component whichs importing CKEditor locally) Then on server side will be just empty div hydrated on client side with real editor.

ThibaultVlacich commented 5 years ago

UPDATE: ok there is one easy workaround for Nuxt. Registering ckeditor component and importing all stuff just from plugin with ssr: false option (or registering there own component whichs importing CKEditor locally) Then on server side will be just empty div hydrated on client side with real editor.

Can you be more specific? It's working indeed for the import of the CKEditor component, but I still have an issue when importing the editor itself in my component.

import ClassicEditor from '@ckeditor/ckeditor5-build-classic' raises an error:

ReferenceError window is not defined

And since it's not possible for whatever reason to define the editors globally because of #9...

farin commented 5 years ago

Can you be more specific?

Yes. Import in node env doesn't work but you can achive thet ckeditor is imported only in client env even your app is universal SSR app.

You need to do following steps:

  1. make plugin which register wrapper around component around ck editor

plugins/rich-editor.js

import Vue from 'vue'
import RichEditor from '@/components/RichEditor'

// register component from plugin to bypass SSR
Vue.component('rich-editor', RichEditor)

2 Register your plugin in nuxt config to be included only on client

nuxt.config.js

plugins: [
  { src: '~/plugins/rich-editor', ssr: false }
],
  1. Create wrapper component

components/RichEditor.vue

<template>
   <ckeditor
      :editor="editor"
      :value="value"
      :config="editorConfig"
     @input="ev => $emit('input', ev)" 
   />
</template>

<script>
import CKEditor from '@ckeditor/ckeditor5-vue'
import ClassicEditor from '@ckeditor/ckeditor5-build-classic'

export default {
  name: 'RichEditor',
  props: {
    value: [String],
    options: {
      type: [Object],
      default: () => {}
    }
  },
  components: {
    ckeditor: CKEditor.component
  },
  data() {
    return {      
      editor: ClassicEditor,
      editorConfig: this.options,
    }
  }
}
</script>
  1. Use your component anywhere you want. Bind content with standard v-model Dont' import RichEditor file there! Don't put RichEditor in components: { ... }.
    <template>
    <div>
    <h1>My Page<h1>
    <rich-editor v-model="content" />
    </div>
    </template>

Because plugin is not loaded on server side, then will be just unknown tag causing no trouble. On client side, it's regular component registered globally by plugin and page will be rendered with CKEditor as wanted..

ThibaultVlacich commented 5 years ago

Thanks, it's working perfectly.

PS: According to the Nuxt documentation, we should use mode: 'client' instead of srr: false [1].

MasterMindLegion commented 4 years ago

@farin Hello there, I humbly thank for this great workaround for Nuxt, but if you can tell me, how to add plugins to Ck editor now? Thank you very

hanzlahabib commented 4 years ago

@farin Hello there, I humbly thank for this great workaround for Nuxt, but if you can tell me, how to add plugins to Ck editor now? Thank you very

You can add a plugin from a web interface and build new CKEditor package for your need, or fork its repo and clone it on local make changes according to docs which state how to load different plugins in the editor, after completing this stuff push your stuff in a new branch and update it on remote, now from this branch, you can publish npm package and use that package in your project

I hope this helps you