diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 7a5e369..72af7ae 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -28,5 +28,14 @@ jobs: - name: Build Demo run: npm run build:demo - - name: Test e2e - run: npm run test:e2e + - name: Run E2E Test + uses: cypress-io/github-action@v5 + with: + start: npm run preview + record: true + browser: chrome + env: + # Recommended: pass the GitHub token lets this action correctly + # determine the unique run id necessary to re-run the checks + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d65c732..92ff789 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -33,7 +33,16 @@ jobs: run: npm run build:demo - name: Run E2E Test - run: npm run test:e2e + uses: cypress-io/github-action@v5 + with: + start: npm run preview + record: true + browser: chrome + env: + # Recommended: pass the GitHub token lets this action correctly + # determine the unique run id necessary to re-run the checks + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }} - name: Deploy to vue-live.surge.sh if: ${{ github.ref == 'refs/heads/master' }} diff --git a/README.md b/README.md index 4b14ee6..e024242 100644 --- a/README.md +++ b/README.md @@ -178,6 +178,15 @@ export default { ``` +### `directives` + +**Type** Object: + +- key: registration name +- value: VueJs directive object + +You can use this prop in the same fashion as `components` above. + ### `requires` **Type** Object: diff --git a/cypress.config.ts b/cypress.config.ts index ef48d11..cc43ce3 100644 --- a/cypress.config.ts +++ b/cypress.config.ts @@ -1,6 +1,7 @@ import { defineConfig } from "cypress"; export default defineConfig({ + projectId: 'wbesaj', e2e: { specPattern: "cypress/e2e/**/*.{cy,spec}.{js,jsx,ts,tsx}", baseUrl: "http://localhost:4173", diff --git a/cypress/e2e/LiveRefresh.cy.ts b/cypress/e2e/LiveRefresh.cy.ts index 193c460..27bfd7f 100644 --- a/cypress/e2e/LiveRefresh.cy.ts +++ b/cypress/e2e/LiveRefresh.cy.ts @@ -10,15 +10,26 @@ describe("Live Refresh", () => { }); it("changes the render after code change", () => { + const textToReplace = "inline component"; + const textReplaced = "red component"; + cy.get("@preview") - .find(".v3dp__datepicker input") - .should("not.have.value", ""); + .get("[data-cy=my-button]") + .should("have.text", textToReplace); - const codeToDelete = ' v-model="today"/>'; cy.get("@container") - .find(".prism-editor-wrapper textarea") - .type(`${"{backspace}".repeat(codeToDelete.length)}/>`); + .find(".prism-editor-wrapper textarea").as("editor"); + + cy.get("@editor").invoke("val") + .then((val) => { + cy.get("@editor") + .clear() + .invoke('val', `${val}`.replace(textToReplace, textReplaced)) + .trigger('input'); - cy.get("@preview").find(".v3dp__datepicker input").should("have.value", ""); + cy.get("@preview") + .get("[data-cy=my-button]") + .should("have.text", textReplaced); + }); }); }); diff --git a/demo/App.vue b/demo/App.vue index c3651e3..d24a104 100644 --- a/demo/App.vue +++ b/demo/App.vue @@ -14,7 +14,7 @@
You can edit this ->
-
@@ -27,8 +27,8 @@ file components as well

-

SFC with setup

- +

VSG partial mode or pure template

+

Pure JavaScript code

Or if you prefer to, use the new Vue() format

@@ -65,6 +65,9 @@

JSX

+

TSX

+ +

Double Root

@@ -92,6 +95,7 @@ import codeSfc from "./assets/Button.vue?raw"; import codeSfcSetup from "./assets/ButtonSetup.vue?raw"; import codeJs from "./assets/input.js?raw"; import realjsx from "./assets/real.jsx?raw"; +import blobtsx from "./assets/blob.tsx?raw"; import codeTemplate from "./assets/PureTemplate.html?raw"; import doubleRoot from "./assets/PureTemplateDoubleRoot.html?raw"; import codeChicago from "./assets/Chicago.jsx?raw"; @@ -116,6 +120,7 @@ export default defineComponent({ CustomLayout: markRaw(CustomLayout), chicagoRequires: { "./chicagoNeighbourhoods": all }, realjsx, + blobtsx, separateCode: codeSfc, doubleRoot, openExamples: false, diff --git a/demo/assets/ButtonSetup.vue b/demo/assets/ButtonSetup.vue index ad928d8..035fc4a 100644 --- a/demo/assets/ButtonSetup.vue +++ b/demo/assets/ButtonSetup.vue @@ -1,5 +1,15 @@ @@ -7,13 +17,12 @@ const msg = ref("Push Me") - - + \ No newline at end of file diff --git a/demo/assets/blob.tsx b/demo/assets/blob.tsx new file mode 100644 index 0000000..51553ec --- /dev/null +++ b/demo/assets/blob.tsx @@ -0,0 +1,12 @@ +const args = { + type: "button", + value: "update me", +} as const; + +type Key = keyof typeof args; + +export default { + render() { + return ; + }, +}; diff --git a/package-lock.json b/package-lock.json index 1e881a8..8242306 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,7 +17,8 @@ "debounce": "^1.2.1", "hash-sum": "^2.0.0", "prismjs": "^1.29.0", - "vue-inbrowser-compiler-sucrase": "^4.60.0", + "vue-inbrowser-compiler-sucrase": "^4.62.0", + "vue-inbrowser-compiler-utils": "^4.62.1", "vue-prism-editor": "^2.0.0-alpha.2" }, "devDependencies": { @@ -4214,23 +4215,23 @@ } }, "node_modules/vue-inbrowser-compiler-independent-utils": { - "version": "4.56.2", - "resolved": "https://registry.npmjs.org/vue-inbrowser-compiler-independent-utils/-/vue-inbrowser-compiler-independent-utils-4.56.2.tgz", - "integrity": "sha512-szE2vZDSkZlItq+K4MevgvCGKt5IzM6OkIjyCuj/09ty2akixeQGNFRXyDELMdmVVzmN+9nJn02YKnoPkhXHwA==", + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/vue-inbrowser-compiler-independent-utils/-/vue-inbrowser-compiler-independent-utils-4.62.0.tgz", + "integrity": "sha512-Xj7phxND9kycttv1KxvzenyqdjVQLf8V5LDREO+x+2FwM99pKhUg2VoxPHlRKghcvOvBv9Fs1iaU9kJCl0DjbQ==", "peerDependencies": { "vue": ">=2" } }, "node_modules/vue-inbrowser-compiler-sucrase": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/vue-inbrowser-compiler-sucrase/-/vue-inbrowser-compiler-sucrase-4.60.0.tgz", - "integrity": "sha512-JTfksUvz4b8UFRJ28iHFh2sH6/6+14Bj5FEiqCmHuFzULiQKN8ZAWTg4WqmDRRLfbmKqNzUpzoIEv8+pyGFMQQ==", + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/vue-inbrowser-compiler-sucrase/-/vue-inbrowser-compiler-sucrase-4.62.0.tgz", + "integrity": "sha512-i/04sJf6PhLQX7Q9orrz7j+8jz/6zNugkPZkTzDVyXT67QclXF7Rg6FcAh90pjfKGTUwsxLaXC9vOIh+ePvWbg==", "dependencies": { "@babel/parser": "^7.13.12", "camelcase": "^5.3.1", "detect-browser": "^5.2.0", "sucrase": "3.29.0", - "vue-inbrowser-compiler-utils": "^4.55.0", + "vue-inbrowser-compiler-utils": "^4.62.0", "walkes": "^0.2.1" }, "peerDependencies": { @@ -4238,13 +4239,13 @@ } }, "node_modules/vue-inbrowser-compiler-utils": { - "version": "4.56.5", - "resolved": "https://registry.npmjs.org/vue-inbrowser-compiler-utils/-/vue-inbrowser-compiler-utils-4.56.5.tgz", - "integrity": "sha512-EYAf8L4ibXJhlPDZDAwDS0eHCek8kU2PoJGrJh0RpIm8AHxuIrLe4mPClWmQ0g9PBNQNdW9JvhWvRpzPD27mmw==", + "version": "4.62.1", + "resolved": "https://registry.npmjs.org/vue-inbrowser-compiler-utils/-/vue-inbrowser-compiler-utils-4.62.1.tgz", + "integrity": "sha512-OBXww64WgfxgAwPalZHWAbrVqcAcmeZbsv74uaOL9eABUAKS6sXEbb+RjSXOjPsgfDONMg9zwCPiMOnPVRXlzQ==", "dependencies": { "camelcase": "^5.3.1", "vue-inbrowser-compiler-demi": "^4.56.5", - "vue-inbrowser-compiler-independent-utils": "^4.55.0" + "vue-inbrowser-compiler-independent-utils": "^4.62.0" }, "peerDependencies": { "vue": ">=2" @@ -7395,32 +7396,32 @@ "requires": {} }, "vue-inbrowser-compiler-independent-utils": { - "version": "4.56.2", - "resolved": "https://registry.npmjs.org/vue-inbrowser-compiler-independent-utils/-/vue-inbrowser-compiler-independent-utils-4.56.2.tgz", - "integrity": "sha512-szE2vZDSkZlItq+K4MevgvCGKt5IzM6OkIjyCuj/09ty2akixeQGNFRXyDELMdmVVzmN+9nJn02YKnoPkhXHwA==", + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/vue-inbrowser-compiler-independent-utils/-/vue-inbrowser-compiler-independent-utils-4.62.0.tgz", + "integrity": "sha512-Xj7phxND9kycttv1KxvzenyqdjVQLf8V5LDREO+x+2FwM99pKhUg2VoxPHlRKghcvOvBv9Fs1iaU9kJCl0DjbQ==", "requires": {} }, "vue-inbrowser-compiler-sucrase": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/vue-inbrowser-compiler-sucrase/-/vue-inbrowser-compiler-sucrase-4.60.0.tgz", - "integrity": "sha512-JTfksUvz4b8UFRJ28iHFh2sH6/6+14Bj5FEiqCmHuFzULiQKN8ZAWTg4WqmDRRLfbmKqNzUpzoIEv8+pyGFMQQ==", + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/vue-inbrowser-compiler-sucrase/-/vue-inbrowser-compiler-sucrase-4.62.0.tgz", + "integrity": "sha512-i/04sJf6PhLQX7Q9orrz7j+8jz/6zNugkPZkTzDVyXT67QclXF7Rg6FcAh90pjfKGTUwsxLaXC9vOIh+ePvWbg==", "requires": { "@babel/parser": "^7.13.12", "camelcase": "^5.3.1", "detect-browser": "^5.2.0", "sucrase": "3.29.0", - "vue-inbrowser-compiler-utils": "^4.55.0", + "vue-inbrowser-compiler-utils": "^4.62.0", "walkes": "^0.2.1" } }, "vue-inbrowser-compiler-utils": { - "version": "4.56.5", - "resolved": "https://registry.npmjs.org/vue-inbrowser-compiler-utils/-/vue-inbrowser-compiler-utils-4.56.5.tgz", - "integrity": "sha512-EYAf8L4ibXJhlPDZDAwDS0eHCek8kU2PoJGrJh0RpIm8AHxuIrLe4mPClWmQ0g9PBNQNdW9JvhWvRpzPD27mmw==", + "version": "4.62.1", + "resolved": "https://registry.npmjs.org/vue-inbrowser-compiler-utils/-/vue-inbrowser-compiler-utils-4.62.1.tgz", + "integrity": "sha512-OBXww64WgfxgAwPalZHWAbrVqcAcmeZbsv74uaOL9eABUAKS6sXEbb+RjSXOjPsgfDONMg9zwCPiMOnPVRXlzQ==", "requires": { "camelcase": "^5.3.1", "vue-inbrowser-compiler-demi": "^4.56.5", - "vue-inbrowser-compiler-independent-utils": "^4.55.0" + "vue-inbrowser-compiler-independent-utils": "^4.62.0" } }, "vue-prism-editor": { diff --git a/package.json b/package.json index 9949562..6ea71fc 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,8 @@ "debounce": "^1.2.1", "hash-sum": "^2.0.0", "prismjs": "^1.29.0", - "vue-inbrowser-compiler-sucrase": "^4.60.0", + "vue-inbrowser-compiler-sucrase": "^4.62.0", + "vue-inbrowser-compiler-utils": "^4.62.1", "vue-prism-editor": "^2.0.0-alpha.2" }, "devDependencies": { diff --git a/src/Editor.vue b/src/Editor.vue index 7dd7ca6..7e2fd5e 100644 --- a/src/Editor.vue +++ b/src/Editor.vue @@ -9,7 +9,7 @@ import debounce from "debounce"; import "vue-prism-editor/dist/prismeditor.min.css"; -import makeHighlight from "./utils/highlight"; +import makeHighlight, { CONFIGURED_LANGS, type CONFIGURED_LANGS_TYPE } from "./utils/highlight"; const UPDATE_DELAY = 300; @@ -35,9 +35,9 @@ export default defineComponent({ default: () => ({}), }, prismLang: { - type: String, + type: String as PropType, default: "html", - validator: (val: string) => ["html", "vsg"].includes(val), + validator: (val: string) => CONFIGURED_LANGS.includes(val as CONFIGURED_LANGS_TYPE), }, jsx: { type: Boolean, @@ -58,7 +58,7 @@ export default defineComponent({ */ stableCode: this.code, highlight: (() => (code: string) => code) as ( - lang: "vue" | "vsg", + lang: CONFIGURED_LANGS_TYPE, jsxInExamples: boolean ) => (code: string, errorLoc: any) => string, }; @@ -73,7 +73,7 @@ export default defineComponent({ }, methods: { highlighter(code: string) { - return this.highlight(this.prismLang as "vue" | "vsg", this.jsx)( + return this.highlight(this.prismLang, this.jsx)( code, this.squiggles && this.error && this.error.loc ); diff --git a/src/Preview.vue b/src/Preview.vue index 8e134e8..c558675 100644 --- a/src/Preview.vue +++ b/src/Preview.vue @@ -143,11 +143,7 @@ export default defineComponent({ } }, async renderComponent(code: string) { - let options = defineComponent({ - render() { - return h("div"); - }, - }); + let options = defineComponent({}); let style; try { const renderedComponent = compileScript( @@ -183,18 +179,6 @@ export default defineComponent({ concatenate, h ) || {})); - if (options.render) { - const preview = this; - const originalRender = options.render; - options.render = function (...args: any[]) { - try { - return originalRender.call(this, ...args); - } catch (e) { - preview.handleError(e); - return; - } - }; - } options.name = "VueLiveCompiledExample"; }; await calcOptions(); @@ -218,7 +202,7 @@ export default defineComponent({ options.data = () => mergeData; } } - + const template = renderedComponent.raw.template if (template) { checkTemplate({ @@ -266,6 +250,8 @@ export default defineComponent({ return; } + console.log({render:options.render}) + this.previewedComponent = markRaw(options); this.iteration = this.iteration + 1; this.error = false; diff --git a/src/VueLive.vue b/src/VueLive.vue index c77b8ed..ad0c9cc 100644 --- a/src/VueLive.vue +++ b/src/VueLive.vue @@ -25,11 +25,12 @@ import hash from "hash-sum"; import Preview from "./Preview.vue"; import Editor from "./Editor.vue"; import VueLiveDefaultLayout from "./VueLiveDefaultLayout.vue"; +import type { CONFIGURED_LANGS_TYPE } from "./utils/highlight"; const LANG_TO_PRISM = { vue: "html", vsg: "vsg", -}; +} as const; const UPDATE_DELAY = 300; @@ -140,7 +141,7 @@ export default defineComponent({ return { model: this.code, lang: "vue", - prismLang: "html", + prismLang: "html" as CONFIGURED_LANGS_TYPE, VueLiveDefaultLayout: markRaw(VueLiveDefaultLayout), /** * this data only gets changed when changing language. diff --git a/src/utils/highlight.ts b/src/utils/highlight.ts index c5db97d..69824e0 100644 --- a/src/utils/highlight.ts +++ b/src/utils/highlight.ts @@ -1,4 +1,4 @@ -// NOTE: this weird way of importing prism is necessary because +// NOTE: this weird way of importing prism is necessary because // prism is not a ESM ready library import pkg from "prismjs"; const { highlight: prismHighlight, languages } = pkg; @@ -8,14 +8,19 @@ import "prismjs/components/prism-markup.js"; import "prismjs/components/prism-javascript.js"; import "prismjs/components/prism-typescript.js"; import "prismjs/components/prism-jsx.js"; +import "prismjs/components/prism-tsx.js"; import "prismjs/components/prism-css.js"; import getScript from "./getScript"; import { parseComponent } from "vue-inbrowser-compiler-sucrase"; +export const CONFIGURED_LANGS = ["html", "vsg", "jsx", "tsx"] as const; +export type CONFIGURED_LANGS_TYPE = (typeof CONFIGURED_LANGS)[number]; + export default async function () { - return function (lang: "vsg" | "vue", jsxInExamples: boolean) { + return function (lang: CONFIGURED_LANGS_TYPE, jsxInExamples: boolean) { if (lang === "vsg") { + // render vsg format return (code: string, errorLoc: any) => { if (!code) { return ""; @@ -23,7 +28,7 @@ export default async function () { const scriptCode = getScript(code, jsxInExamples); const scriptCodeHighlighted = prismHighlight( scriptCode, - languages[jsxInExamples ? "jsx" : "js"], + languages[jsxInExamples ? "tsx" : "ts"], lang ); if (code.length === scriptCode.length) { @@ -45,7 +50,8 @@ export default async function () { templateHighlighted ); }; - } else { + } else if (lang === "html") { + // render vue SFC component format const langScheme = languages.html; return (code: string) => { @@ -70,6 +76,12 @@ export default async function () { ) : htmlHighlighted; }; + } else { + // all other formats + const langScheme = languages[lang]; + return (code: string) => { + return prismHighlight(code, langScheme, lang); + }; } }; } diff --git a/tsconfig.app.json b/tsconfig.app.json index 2d83f5e..d595710 100644 --- a/tsconfig.app.json +++ b/tsconfig.app.json @@ -5,12 +5,14 @@ "src/**/*", "src/**/*.vue", "demo/**/*.vue", - "demo/**/*.ts" + "demo/**/*.ts", + "demo/**/*.tsx" ], "exclude": ["src/**/__tests__/*"], "compilerOptions": { "composite": true, "rootDir": ".", - "noEmit": true + "noEmit": true, + "jsx":"preserve" } }