From 1d3138f0627f14c86a120f0a77fd8da6cd3bcedb Mon Sep 17 00:00:00 2001 From: birdguo Date: Mon, 25 Sep 2023 15:08:30 +0800 Subject: [PATCH 01/10] feat(vue-next): commit ssr init version --- driver/js/.eslintrc.js | 3 + .../hippy-vue-next-ssr-demo/.gitignore | 21 + .../examples/hippy-vue-next-ssr-demo/.npmrc | 1 + .../hippy-vue-next-ssr-demo/README.md | 46 ++ .../examples/hippy-vue-next-ssr-demo/app.d.ts | 10 + .../hippy-vue-next-ssr-demo/package.json | 85 +++ .../webpack-ssr-config/client.android.js | 3 + .../client.android.vendor.js | 108 ++++ .../scripts/webpack-ssr-config/client.base.js | 181 ++++++ .../scripts/webpack-ssr-config/client.dev.js | 195 +++++++ .../webpack-ssr-config/client.entry.js | 97 ++++ .../scripts/webpack-ssr-config/client.ios.js | 3 + .../webpack-ssr-config/client.ios.vendor.js | 108 ++++ .../scripts/webpack-ssr-config/server.dev.js | 186 ++++++ .../webpack-ssr-config/server.entry.js | 177 ++++++ .../scripts/webpack.ssr.build.js | 109 ++++ .../scripts/webpack.ssr.dev.js | 39 ++ .../hippy-vue-next-ssr-demo/server.ts | 80 +++ .../hippy-vue-next-ssr-demo/tsconfig.json | 35 ++ driver/js/package.json | 5 +- .../hippy-vue-next-compiler-ssr/README.md | 6 + .../__test__/component.test.ts | 132 +++++ .../__test__/element.test.ts | 245 ++++++++ .../__test__/native-component.test.ts | 26 + .../__test__/setup.ts | 19 + .../__test__/slot-out.test.ts | 56 ++ .../__test__/utils.ts | 38 ++ .../__test__/vue-built-in.test.ts | 58 ++ .../api-extractor.json | 38 ++ .../package-lock.json | 115 ++++ .../hippy-vue-next-compiler-ssr/package.json | 49 ++ .../hippy-vue-next-compiler-ssr/src/errors.ts | 54 ++ .../hippy-vue-next-compiler-ssr/src/index.ts | 122 ++++ .../src/runtimeHelpers.ts | 70 +++ .../src/ssrCodegenTransform.ts | 262 +++++++++ .../src/transforms/ssrInjectCssVars.ts | 91 +++ .../transforms/ssrInjectFallthroughAttrs.ts | 111 ++++ .../src/transforms/ssrTransformComponent.ts | 400 +++++++++++++ .../src/transforms/ssrTransformElement.ts | 477 ++++++++++++++++ .../src/transforms/ssrTransformSlotOutlet.ts | 113 ++++ .../src/transforms/ssrTransformSuspense.ts | 102 ++++ .../src/transforms/ssrTransformTeleport.ts | 93 +++ .../transforms/ssrTransformTransitionGroup.ts | 128 +++++ .../src/transforms/ssrVFor.ts | 70 +++ .../src/transforms/ssrVIf.ts | 102 ++++ .../src/transforms/ssrVModel.ts | 184 ++++++ .../src/transforms/ssrVShow.ts | 54 ++ .../hippy-vue-next-server-renderer/README.md | 6 + .../__test__/native.test.ts | 102 ++++ .../__test__/render-attr.test.ts | 51 ++ .../__test__/render-vnode.test.ts | 528 ++++++++++++++++++ .../__test__/renderer.test.ts | 148 +++++ .../__test__/setup.ts | 19 + .../__test__/util.test.ts | 33 ++ .../api-extractor.json | 38 ++ .../package-lock.json | 435 +++++++++++++++ .../package.json | 54 ++ .../src/index.ts | 117 ++++ .../src/native.ts | 103 ++++ .../src/render-attrs.ts | 102 ++++ .../src/render-component.ts | 48 ++ .../src/render-vnode.ts | 511 +++++++++++++++++ .../src/renderer.ts | 490 ++++++++++++++++ .../src/util.ts | 80 +++ .../__test__/setup.ts | 80 +++ .../__test__/style-match/css-append.test.ts | 128 +++++ .../__test__/style-match}/index.test.ts | 119 +++- .../__test__/style-match}/parser.test.ts | 12 +- .../style-parser/color-parser.test.ts | 68 +++ .../__test__/style-parser/css-parser.test.ts | 118 ++++ .../hippy-vue-next-style-parser/package.json | 25 +- .../hippy-vue-next-style-parser/src/index.ts | 6 + .../src/style-match/css-append.ts | 386 +++++++++++++ .../src/style-match/css-map.ts | 247 ++++++++ .../src/style-match}/css-selectors-match.ts | 40 +- .../src/style-match}/css-selectors.ts | 119 ++-- .../src/style-match/index.ts | 71 +++ .../src/style-match}/parser.ts | 5 +- .../__test__/built-in-component.test.ts | 8 +- .../__test__/native-component/index.test.ts | 45 ++ .../hippy-vue-next/__test__/node-ops.test.ts | 7 +- .../__test__/patch-prop.test.ts | 7 +- .../__test__/runtime/component/index.test.ts | 7 +- .../runtime/document/hippy-document.test.ts | 7 +- .../before-render-to-native-hook.test.ts | 3 + .../element/hippy-comment-element.test.ts | 3 + .../runtime/element/hippy-element.test.ts | 76 ++- .../element/hippy-input-element.test.ts | 3 + .../element/hippy-list-element.test.ts | 3 + .../__test__/runtime/event/event-bus.test.ts | 7 +- .../event/hippy-event-dispatcher.test.ts | 9 +- .../runtime/event/hippy-event-target.test.ts | 13 +- .../runtime/event/hippy-event.test.ts | 7 +- .../__test__/runtime/native/index.test.ts | 7 +- .../__test__/runtime/node/hipyy-node.test.ts | 34 +- .../__test__/runtime/render/index.test.ts | 8 +- .../__test__/runtime/text/hippy-text.test.ts | 4 + .../__test__/util/event.test.ts | 6 +- .../hippy-vue-next/__test__/util/i18n.test.ts | 7 +- .../__test__/util/index.test.ts | 13 +- .../util/{node-cache.test.ts => node.test.ts} | 6 +- .../hippy-vue-next/__test__/util/rem.test.ts | 7 +- .../__test__/util/screen.test.ts | 7 +- .../js/packages/hippy-vue-next/package.json | 7 +- .../hippy-vue-next/src/config/index.ts | 6 + .../packages/hippy-vue-next/src/hydration.ts | 80 +++ .../js/packages/hippy-vue-next/src/index.ts | 118 ++-- .../src/native-component/animation.ts | 8 + .../src/native-component/index.ts | 27 + .../src/native-component/swiper.ts | 9 +- .../runtime/element/hippy-comment-element.ts | 9 +- .../src/runtime/element/hippy-element.ts | 123 +++- .../src/runtime/native/index.ts | 36 +- .../src/runtime/node/hippy-node.ts | 47 +- .../src/runtime/style/css-map.ts | 71 --- .../hippy-vue-next/src/runtime/style/index.ts | 136 ----- .../src/runtime/text/hippy-text.ts | 8 +- .../src/runtime/websocket/websocket.ts | 3 +- .../hippy-vue-next/src/types/index.ts | 32 ++ .../packages/hippy-vue-next/src/util/index.ts | 25 +- driver/js/scripts/vue-next-configs.js | 125 +++-- driver/js/tsconfig.json | 2 + 122 files changed, 9625 insertions(+), 547 deletions(-) create mode 100644 driver/js/examples/hippy-vue-next-ssr-demo/.gitignore create mode 100644 driver/js/examples/hippy-vue-next-ssr-demo/.npmrc create mode 100644 driver/js/examples/hippy-vue-next-ssr-demo/README.md create mode 100644 driver/js/examples/hippy-vue-next-ssr-demo/app.d.ts create mode 100644 driver/js/examples/hippy-vue-next-ssr-demo/package.json create mode 100644 driver/js/examples/hippy-vue-next-ssr-demo/scripts/webpack-ssr-config/client.android.js create mode 100644 driver/js/examples/hippy-vue-next-ssr-demo/scripts/webpack-ssr-config/client.android.vendor.js create mode 100644 driver/js/examples/hippy-vue-next-ssr-demo/scripts/webpack-ssr-config/client.base.js create mode 100644 driver/js/examples/hippy-vue-next-ssr-demo/scripts/webpack-ssr-config/client.dev.js create mode 100644 driver/js/examples/hippy-vue-next-ssr-demo/scripts/webpack-ssr-config/client.entry.js create mode 100644 driver/js/examples/hippy-vue-next-ssr-demo/scripts/webpack-ssr-config/client.ios.js create mode 100644 driver/js/examples/hippy-vue-next-ssr-demo/scripts/webpack-ssr-config/client.ios.vendor.js create mode 100644 driver/js/examples/hippy-vue-next-ssr-demo/scripts/webpack-ssr-config/server.dev.js create mode 100644 driver/js/examples/hippy-vue-next-ssr-demo/scripts/webpack-ssr-config/server.entry.js create mode 100644 driver/js/examples/hippy-vue-next-ssr-demo/scripts/webpack.ssr.build.js create mode 100644 driver/js/examples/hippy-vue-next-ssr-demo/scripts/webpack.ssr.dev.js create mode 100644 driver/js/examples/hippy-vue-next-ssr-demo/server.ts create mode 100644 driver/js/examples/hippy-vue-next-ssr-demo/tsconfig.json create mode 100644 driver/js/packages/hippy-vue-next-compiler-ssr/README.md create mode 100644 driver/js/packages/hippy-vue-next-compiler-ssr/__test__/component.test.ts create mode 100644 driver/js/packages/hippy-vue-next-compiler-ssr/__test__/element.test.ts create mode 100644 driver/js/packages/hippy-vue-next-compiler-ssr/__test__/native-component.test.ts create mode 100644 driver/js/packages/hippy-vue-next-compiler-ssr/__test__/setup.ts create mode 100644 driver/js/packages/hippy-vue-next-compiler-ssr/__test__/slot-out.test.ts create mode 100644 driver/js/packages/hippy-vue-next-compiler-ssr/__test__/utils.ts create mode 100644 driver/js/packages/hippy-vue-next-compiler-ssr/__test__/vue-built-in.test.ts create mode 100644 driver/js/packages/hippy-vue-next-compiler-ssr/api-extractor.json create mode 100644 driver/js/packages/hippy-vue-next-compiler-ssr/package-lock.json create mode 100644 driver/js/packages/hippy-vue-next-compiler-ssr/package.json create mode 100644 driver/js/packages/hippy-vue-next-compiler-ssr/src/errors.ts create mode 100644 driver/js/packages/hippy-vue-next-compiler-ssr/src/index.ts create mode 100644 driver/js/packages/hippy-vue-next-compiler-ssr/src/runtimeHelpers.ts create mode 100644 driver/js/packages/hippy-vue-next-compiler-ssr/src/ssrCodegenTransform.ts create mode 100644 driver/js/packages/hippy-vue-next-compiler-ssr/src/transforms/ssrInjectCssVars.ts create mode 100644 driver/js/packages/hippy-vue-next-compiler-ssr/src/transforms/ssrInjectFallthroughAttrs.ts create mode 100644 driver/js/packages/hippy-vue-next-compiler-ssr/src/transforms/ssrTransformComponent.ts create mode 100644 driver/js/packages/hippy-vue-next-compiler-ssr/src/transforms/ssrTransformElement.ts create mode 100644 driver/js/packages/hippy-vue-next-compiler-ssr/src/transforms/ssrTransformSlotOutlet.ts create mode 100644 driver/js/packages/hippy-vue-next-compiler-ssr/src/transforms/ssrTransformSuspense.ts create mode 100644 driver/js/packages/hippy-vue-next-compiler-ssr/src/transforms/ssrTransformTeleport.ts create mode 100644 driver/js/packages/hippy-vue-next-compiler-ssr/src/transforms/ssrTransformTransitionGroup.ts create mode 100644 driver/js/packages/hippy-vue-next-compiler-ssr/src/transforms/ssrVFor.ts create mode 100644 driver/js/packages/hippy-vue-next-compiler-ssr/src/transforms/ssrVIf.ts create mode 100644 driver/js/packages/hippy-vue-next-compiler-ssr/src/transforms/ssrVModel.ts create mode 100644 driver/js/packages/hippy-vue-next-compiler-ssr/src/transforms/ssrVShow.ts create mode 100644 driver/js/packages/hippy-vue-next-server-renderer/README.md create mode 100644 driver/js/packages/hippy-vue-next-server-renderer/__test__/native.test.ts create mode 100644 driver/js/packages/hippy-vue-next-server-renderer/__test__/render-attr.test.ts create mode 100644 driver/js/packages/hippy-vue-next-server-renderer/__test__/render-vnode.test.ts create mode 100644 driver/js/packages/hippy-vue-next-server-renderer/__test__/renderer.test.ts create mode 100644 driver/js/packages/hippy-vue-next-server-renderer/__test__/setup.ts create mode 100644 driver/js/packages/hippy-vue-next-server-renderer/__test__/util.test.ts create mode 100644 driver/js/packages/hippy-vue-next-server-renderer/api-extractor.json create mode 100644 driver/js/packages/hippy-vue-next-server-renderer/package-lock.json create mode 100644 driver/js/packages/hippy-vue-next-server-renderer/package.json create mode 100644 driver/js/packages/hippy-vue-next-server-renderer/src/index.ts create mode 100644 driver/js/packages/hippy-vue-next-server-renderer/src/native.ts create mode 100644 driver/js/packages/hippy-vue-next-server-renderer/src/render-attrs.ts create mode 100644 driver/js/packages/hippy-vue-next-server-renderer/src/render-component.ts create mode 100644 driver/js/packages/hippy-vue-next-server-renderer/src/render-vnode.ts create mode 100644 driver/js/packages/hippy-vue-next-server-renderer/src/renderer.ts create mode 100644 driver/js/packages/hippy-vue-next-server-renderer/src/util.ts create mode 100644 driver/js/packages/hippy-vue-next-style-parser/__test__/setup.ts create mode 100644 driver/js/packages/hippy-vue-next-style-parser/__test__/style-match/css-append.test.ts rename driver/js/packages/{hippy-vue-next/__test__/runtime/style => hippy-vue-next-style-parser/__test__/style-match}/index.test.ts (80%) rename driver/js/packages/{hippy-vue-next/__test__/runtime/style => hippy-vue-next-style-parser/__test__/style-match}/parser.test.ts (92%) create mode 100644 driver/js/packages/hippy-vue-next-style-parser/__test__/style-parser/color-parser.test.ts create mode 100644 driver/js/packages/hippy-vue-next-style-parser/__test__/style-parser/css-parser.test.ts create mode 100644 driver/js/packages/hippy-vue-next-style-parser/src/style-match/css-append.ts create mode 100644 driver/js/packages/hippy-vue-next-style-parser/src/style-match/css-map.ts rename driver/js/packages/{hippy-vue-next/src/runtime/style => hippy-vue-next-style-parser/src/style-match}/css-selectors-match.ts (84%) rename driver/js/packages/{hippy-vue-next/src/runtime/style => hippy-vue-next-style-parser/src/style-match}/css-selectors.ts (82%) create mode 100644 driver/js/packages/hippy-vue-next-style-parser/src/style-match/index.ts rename driver/js/packages/{hippy-vue-next/src/runtime/style => hippy-vue-next-style-parser/src/style-match}/parser.ts (98%) create mode 100644 driver/js/packages/hippy-vue-next/__test__/native-component/index.test.ts rename driver/js/packages/hippy-vue-next/__test__/util/{node-cache.test.ts => node.test.ts} (96%) create mode 100644 driver/js/packages/hippy-vue-next/src/hydration.ts delete mode 100644 driver/js/packages/hippy-vue-next/src/runtime/style/css-map.ts delete mode 100644 driver/js/packages/hippy-vue-next/src/runtime/style/index.ts diff --git a/driver/js/.eslintrc.js b/driver/js/.eslintrc.js index 99c8ddf95ee..d4ef3af80b6 100644 --- a/driver/js/.eslintrc.js +++ b/driver/js/.eslintrc.js @@ -68,6 +68,7 @@ module.exports = { '@typescript-eslint/consistent-type-assertions': 'off', '@typescript-eslint/naming-convention': 'off', '@typescript-eslint/prefer-for-of': 'off', + '@typescript-eslint/no-require-imports': 'off', }, parserOptions: { project: ['./**/tsconfig.json'], @@ -171,6 +172,8 @@ module.exports = { ['sfc', resolveVue('sfc')], ['he', path.resolve(__dirname, './packages/hippy-vue/src/util/entity-decoder')], ['@hippy-vue-next-style-parser', resolvePackage('hippy-vue-next-style-parser')], + ['@hippy-vue-next', resolvePackage('hippy-vue-next')], + ['@hippy-vue-next-server-renderer', resolvePackage('hippy-vue-next-server-renderer')], ], }, }, diff --git a/driver/js/examples/hippy-vue-next-ssr-demo/.gitignore b/driver/js/examples/hippy-vue-next-ssr-demo/.gitignore new file mode 100644 index 00000000000..a0dddc6fb8c --- /dev/null +++ b/driver/js/examples/hippy-vue-next-ssr-demo/.gitignore @@ -0,0 +1,21 @@ +.DS_Store +node_modules +/dist + +# local env files +.env.local +.env.*.local + +# Log files +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Editor directories and files +.idea +.vscode +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/driver/js/examples/hippy-vue-next-ssr-demo/.npmrc b/driver/js/examples/hippy-vue-next-ssr-demo/.npmrc new file mode 100644 index 00000000000..43c97e719a5 --- /dev/null +++ b/driver/js/examples/hippy-vue-next-ssr-demo/.npmrc @@ -0,0 +1 @@ +package-lock=false diff --git a/driver/js/examples/hippy-vue-next-ssr-demo/README.md b/driver/js/examples/hippy-vue-next-ssr-demo/README.md new file mode 100644 index 00000000000..d21b211e7a4 --- /dev/null +++ b/driver/js/examples/hippy-vue-next-ssr-demo/README.md @@ -0,0 +1,46 @@ +# @hippy/vue-next demo + + +### Introduction +This package is the demo project for @hippy/vue-next. Project include most use case for +@hippy/vue-next. Just try it. + +### Usage +Read the hippy framework [doc](https://github.com/Tencent/Hippy/blob/master/README.md#-getting-started) and learn +how to use. + +### How To Use SSR + +we were support SSR for @hippy/vue-next. here is only how to use SSR. how to use vue-next doc is [here](https://hippyjs.org/en-us/#/hippy-vue/vue3) + +1. Before running vue-next-ssr-demo, you should run `npm run init` at root directory to install dependencies and build front-end sdk packages. +2. Then run `cd examples/hippy-vue-next-demo` and `npm install --legacy-peer-deps` to install demo dependencies. + +Now determine which environment you want build + +> Because our server listening port 8080, so if you are using android device, you should run `adb reverse tcp:8080 tcp:8080` +> to forward mobile device port to pc port, iOS simulator doesn't need this step. + +ensure you were at `examples/hippy-vue-next-demo`. + +#### Development + +1. run `npm run ssr:dev-build` to build client entry & client bundle, then running hippy debug server +2. run `npm run ssr:dev-server` to build server bundle and start SSR web server to listen port **8080**. +3. debug your app with [reference](https://hippyjs.org/en-us/#/guide/debug) +> You can change server listen port 8080 in `server.ts` by your self, but you also need change request port 8080 in +> `src/main-client.ts` and modify the adb reverse port, ensure port is same at three place + +#### Production + +1. run `npm run ssr:prod-build` to build client entry, server bundle, client bundle +2. run `npm run ssr:prod-server` to start SSR web server to listen port **8080**. +3. test your app +> In production, you can use process manage tool to manage your NodeJs process, like pm2. +> +> And you should deploy you web server at real server with real domain, then you can request +> SSR cgi like https://xxx.com/getSsrFirstScreenData +> + +#### Tips +> Usage of non SSR is [here](https://hippyjs.org/en-us/#/guide/integration) diff --git a/driver/js/examples/hippy-vue-next-ssr-demo/app.d.ts b/driver/js/examples/hippy-vue-next-ssr-demo/app.d.ts new file mode 100644 index 00000000000..d614236bf60 --- /dev/null +++ b/driver/js/examples/hippy-vue-next-ssr-demo/app.d.ts @@ -0,0 +1,10 @@ +declare module '*.jpg'; +declare module '*.png'; +declare module '*.vue' { + import { defineComponent } from 'vue'; + const Component: ReturnType; + export default Component; +} + +type NeedToTyped = any; + diff --git a/driver/js/examples/hippy-vue-next-ssr-demo/package.json b/driver/js/examples/hippy-vue-next-ssr-demo/package.json new file mode 100644 index 00000000000..252da99ea5a --- /dev/null +++ b/driver/js/examples/hippy-vue-next-ssr-demo/package.json @@ -0,0 +1,85 @@ +{ + "name": "hippy-vue-next-demo", + "version": "2.0.0", + "description": "A Demo Example For Hippy-Vue-Next Library To Show.", + "private": true, + "webMain": "./src/main-web.ts", + "nativeMain": "./src/main-native.ts", + "serverMain": "./src/main-server.ts", + "serverEntry": "./server.ts", + "ssrMain": "./src/main-client.ts", + "repository": "https://github.com/Tencent/Hippy/tree/master/examples/hippy-vue-next-demo", + "license": "Apache-2.0", + "author": "OpenHippy Team", + "scripts": { + "hippy:dev": "node ./scripts/env-polyfill.js hippy-dev -c ./scripts/hippy-webpack.dev.js", + "hippy:vendor": "node ./scripts/env-polyfill.js webpack --config ./scripts/hippy-webpack.ios-vendor.js --config ./scripts/hippy-webpack.android-vendor.js", + "hippy:build": "node ./scripts/env-polyfill.js webpack --config ./scripts/hippy-webpack.ios.js --config ./scripts/hippy-webpack.android.js", + "web:dev": "npm run hippy:dev & node ./scripts/env-polyfill.js webpack serve --config ./scripts/hippy-webpack.web-renderer.dev.js", + "web:build": "node ./scripts/env-polyfill.js webpack --config ./scripts/hippy-webpack.web-renderer.js", + "ssr:dev-client": "node ./scripts/env-polyfill.js hippy-dev -c ./scripts/webpack-ssr-config/client.dev.js", + "ssr:dev-server": "node ./scripts/env-polyfill.js && node ./scripts/webpack.ssr.dev.js", + "ssr:prod-build": "node ./scripts/webpack.ssr.build.js", + "ssr:prod-server": "node ./dist/server/index.js --mode production" + }, + "dependencies": { + "@hippy/vue-router-next-history": "latest", + "@hippy/web-renderer": "latest", + "@hippy/vue-next": "latest", + "@hippy/vue-next-server-renderer": "file:../../packages/hippy-vue-next-server-renderer", + "@hippy/vue-next-style-parser": "file:../../packages/hippy-vue-next-style-parser", + "@vue/runtime-core": "^3.2.46", + "@vue/shared": "^3.2.46", + "core-js": "^3.20.2", + "vue": "^3.2.46", + "vue-router": "^4.0.12", + "express": "^4.18.2", + "pinia": "2.0.30" + }, + "devDependencies": { + "@babel/core": "^7.12.0", + "@babel/plugin-proposal-class-properties": "^7.10.4", + "@babel/plugin-proposal-decorators": "^7.10.5", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.10.4", + "@babel/plugin-proposal-object-rest-spread": "^7.5.5", + "@babel/plugin-proposal-optional-chaining": "^7.10.4", + "@babel/plugin-transform-async-to-generator": "^7.5.0", + "@babel/plugin-transform-runtime": "^7.11.0", + "@babel/polyfill": "^7.12.0", + "@babel/preset-env": "^7.12.0", + "@babel/runtime": "^7.16.0", + "@hippy/debug-server-next": "latest", + "@hippy/hippy-dynamic-import-plugin": "^2.0.0", + "@hippy/hippy-hmr-plugin": "^0.1.0", + "@hippy/rejection-tracking-polyfill": "^1.0.0", + "@hippy/vue-css-loader": "^2.0.1", + "@vitejs/plugin-vue": "^1.9.4", + "@hippy/vue-next-compiler-ssr": "file:../../packages/hippy-vue-next-compiler-ssr", + "@types/shelljs": "^0.8.5", + "@vue/cli-service": "^4.5.19", + "@vue/compiler-sfc": "^3.2.46", + "babel-loader": "^8.1.0", + "case-sensitive-paths-webpack-plugin": "^2.2.0", + "chokidar": "^3.5.3", + "clean-webpack-plugin": "^4.0.0", + "webpack-manifest-plugin": "^4.1.1", + "cross-env": "^7.0.3", + "cross-env-os": "^7.1.1", + "esbuild": "^0.13.14", + "esbuild-loader": "^2.18.0", + "file-loader": "^4.3.0", + "less": "^4.1.2", + "less-loader": "^7.1.0", + "shelljs": "^0.8.5", + "terser": "^4.8.0", + "ts-loader": "^8.4.0", + "@types/express": "^4.17.17", + "url-loader": "^4.0.0", + "vue-loader": "^17.0.0", + "webpack": "^4.46.0", + "webpack-cli": "^4.7.2" + }, + "engines": { + "node": ">=15" + } +} diff --git a/driver/js/examples/hippy-vue-next-ssr-demo/scripts/webpack-ssr-config/client.android.js b/driver/js/examples/hippy-vue-next-ssr-demo/scripts/webpack-ssr-config/client.android.js new file mode 100644 index 00000000000..bfd96dce07b --- /dev/null +++ b/driver/js/examples/hippy-vue-next-ssr-demo/scripts/webpack-ssr-config/client.android.js @@ -0,0 +1,3 @@ +const { getWebpackSsrBaseConfig } = require('./client.base'); + +module.exports = getWebpackSsrBaseConfig('android', 'production'); diff --git a/driver/js/examples/hippy-vue-next-ssr-demo/scripts/webpack-ssr-config/client.android.vendor.js b/driver/js/examples/hippy-vue-next-ssr-demo/scripts/webpack-ssr-config/client.android.vendor.js new file mode 100644 index 00000000000..85e8b860784 --- /dev/null +++ b/driver/js/examples/hippy-vue-next-ssr-demo/scripts/webpack-ssr-config/client.android.vendor.js @@ -0,0 +1,108 @@ +const fs = require('fs'); +const path = require('path'); +const webpack = require('webpack'); +const { VueLoaderPlugin } = require('vue-loader'); +const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin'); + +const platform = 'android'; + +module.exports = { + mode: 'production', + bail: true, + entry: { + vendor: [path.resolve(__dirname, '../vendor.js')], + }, + output: { + filename: `[name].${platform}.js`, + path: path.resolve(`./dist/${platform}/`), + globalObject: '(0, eval)("this")', + library: 'hippyVueBase', + }, + plugins: [ + new webpack.NamedModulesPlugin(), + new webpack.DefinePlugin({ + 'process.env.NODE_ENV': JSON.stringify('production'), + __PLATFORM__: JSON.stringify(platform), + }), + new CaseSensitivePathsPlugin(), + new VueLoaderPlugin(), + new webpack.DllPlugin({ + context: path.resolve(__dirname, '../..'), + path: path.resolve(__dirname, `../../dist/${platform}/[name]-manifest.json`), + name: 'hippyVueBase', + }), + ], + module: { + rules: [ + { + test: /\.vue$/, + use: [ + { + loader: 'vue-loader', + options: { + compilerOptions: { + // disable vue3 dom patch flag,because hippy do not support innerHTML + hoistStatic: false, + // whitespace handler, default is 'condense', it can be set 'preserve' + whitespace: 'condense', + }, + }, + }, + ], + }, + { + test: /\.(js)$/, + use: [ + { + loader: 'babel-loader', + options: { + presets: [ + [ + '@babel/preset-env', + { + targets: { + chrome: 57, + }, + }, + ], + ], + plugins: [ + ['@babel/plugin-proposal-class-properties'], + ], + }, + }, + ], + }, + ], + }, + resolve: { + extensions: ['.js', '.vue', '.json', '.ts'], + alias: (() => { + const aliases = { + src: path.resolve('./src'), + }; + + // If @vue/runtime-core was built exist in packages directory then make an alias + // Remove the section if you don't use it + const hippyVueRuntimeCorePath = path.resolve(__dirname, '../../../../packages/hippy-vue-next/node_modules/@vue/runtime-core'); + if (fs.existsSync(path.resolve(hippyVueRuntimeCorePath, 'index.js'))) { + console.warn(`* Using the @vue/runtime-core in ${hippyVueRuntimeCorePath} as vue alias`); + aliases['@vue/runtime-core'] = hippyVueRuntimeCorePath; + } else { + console.warn('* Using the @vue/runtime-core defined in package.json'); + } + + // If @hippy/vue-next was built exist in packages directory then make an alias + // Remove the section if you don't use it + const hippyVueNextPath = path.resolve(__dirname, '../../../../packages/hippy-vue-next/dist'); + if (fs.existsSync(path.resolve(hippyVueNextPath, 'index.js'))) { + console.warn(`* Using the @hippy/vue-next in ${hippyVueNextPath} as @hippy/vue-next alias`); + aliases['@hippy/vue-next'] = hippyVueNextPath; + } else { + console.warn('* Using the @hippy/vue-next defined in package.json'); + } + + return aliases; + })(), + }, +}; diff --git a/driver/js/examples/hippy-vue-next-ssr-demo/scripts/webpack-ssr-config/client.base.js b/driver/js/examples/hippy-vue-next-ssr-demo/scripts/webpack-ssr-config/client.base.js new file mode 100644 index 00000000000..36b6537e19d --- /dev/null +++ b/driver/js/examples/hippy-vue-next-ssr-demo/scripts/webpack-ssr-config/client.base.js @@ -0,0 +1,181 @@ +const path = require('path'); +const fs = require('fs'); +const HippyDynamicImportPlugin = require('@hippy/hippy-dynamic-import-plugin'); +const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin'); +const { VueLoaderPlugin } = require('vue-loader'); +const { WebpackManifestPlugin } = require('webpack-manifest-plugin'); +const webpack = require('webpack'); + +const pkg = require('../../package.json'); + +let cssLoader = '@hippy/vue-css-loader'; +const hippyVueCssLoaderPath = path.resolve(__dirname, '../../../../packages/hippy-vue-css-loader/dist/css-loader.js'); +if (fs.existsSync(hippyVueCssLoaderPath)) { + console.warn(`* Using the @hippy/vue-css-loader in ${hippyVueCssLoaderPath}`); + cssLoader = hippyVueCssLoaderPath; +} else { + console.warn('* Using the @hippy/vue-css-loader defined in package.json'); +} + +/** + * get webpack ssr base config + * + * @param platform build platform + * @param env build environment + */ +exports.getWebpackSsrBaseConfig = function (platform, env) { + // do not generate vendor at development + const manifest = require(`../../dist/${platform}/vendor-manifest.json`); + return { + mode: env, + bail: true, + devtool: false, + entry: { + home: [path.resolve(pkg.nativeMain)], + }, + output: { + filename: `[name].${platform}.js`, + path: path.resolve(`./dist/${platform}/`), + globalObject: '(0, eval)("this")', + // CDN path can be configured to load children bundles from remote server + // publicPath: 'https://xxx/hippy/hippyVueNextDemo/', + }, + plugins: [ + new webpack.NamedModulesPlugin(), + new webpack.DefinePlugin({ + 'process.env': { + NODE_ENV: JSON.stringify(env), + }, + __VUE_OPTIONS_API__: true, + __VUE_PROD_DEVTOOLS__: false, + __PLATFORM__: JSON.stringify(platform), + }), + new CaseSensitivePathsPlugin(), + new VueLoaderPlugin(), + new HippyDynamicImportPlugin(), + new WebpackManifestPlugin({ + fileName: `manifest.${platform}.json`, + }), + new webpack.DllReferencePlugin({ + context: path.resolve(__dirname, '../..'), + manifest, + }), + ], + module: { + rules: [ + { + test: /\.vue$/, + use: [ + { + loader: 'vue-loader', + options: { + compilerOptions: { + // disable vue3 dom patch flag,because hippy do not support innerHTML + hoistStatic: false, + // whitespace handler, default is 'condense', it can be set 'preserve' + whitespace: 'condense', + // do not generate html comment node + comments: false, + }, + }, + }, + ], + }, + { + test: /\.(le|c)ss$/, + use: [cssLoader, 'less-loader'], + }, + { + test: /\.t|js$/, + use: [ + { + loader: 'babel-loader', + options: { + sourceType: 'unambiguous', + presets: [ + [ + '@babel/preset-env', + { + targets: platform === 'android' ? { + chrome: 57, + } : { + ios: 9, + }, + }, + ], + ], + plugins: [ + ['@babel/plugin-proposal-class-properties'], + ['@babel/plugin-proposal-decorators', { legacy: true }], + ['@babel/plugin-transform-runtime', { regenerator: true }], + ], + }, + }, + ], + }, + { + test: /\.(png|jpe?g|gif)$/i, + use: [{ + loader: 'url-loader', + options: { + // if you would like to use base64 for picture, uncomment limit: true + // limit: true, + limit: 1024, + fallback: 'file-loader', + name: '[name].[ext]', + outputPath: 'assets/', + }, + }], + }, + { + test: /\.(ts)$/, + use: [ + { + loader: 'ts-loader', + options: { + transpileOnly: true, + appendTsSuffixTo: [/\.vue$/], + }, + }, + ], + exclude: /node_modules/, + }, + { + test: /\.mjs$/, + include: /node_modules/, + type: 'javascript/auto', + }, + ], + }, + resolve: { + extensions: ['.js', '.vue', '.json', '.ts'], + alias: (() => { + const aliases = { + src: path.resolve('./src'), + }; + + // If @vue/runtime-core was built exist in packages directory then make an alias + // Remove the section if you don't use it + const hippyVueRuntimeCorePath = path.resolve(__dirname, '../../../../packages/hippy-vue-next/node_modules/@vue/runtime-core'); + if (fs.existsSync(path.resolve(hippyVueRuntimeCorePath, 'index.js'))) { + console.warn(`* Using the @vue/runtime-core in ${hippyVueRuntimeCorePath} as vue alias`); + aliases['@vue/runtime-core'] = hippyVueRuntimeCorePath; + } else { + console.warn('* Using the @vue/runtime-core defined in package.json'); + } + + // If @hippy/vue-next was built exist in packages directory then make an alias + // Remove the section if you don't use it + const hippyVueNextPath = path.resolve(__dirname, '../../../../packages/hippy-vue-next/dist'); + if (fs.existsSync(path.resolve(hippyVueNextPath, 'index.js'))) { + console.warn(`* Using the @hippy/vue-next in ${hippyVueNextPath} as @hippy/vue-next alias`); + aliases['@hippy/vue-next'] = hippyVueNextPath; + } else { + console.warn('* Using the @hippy/vue-next defined in package.json'); + } + + return aliases; + })(), + }, + }; +}; diff --git a/driver/js/examples/hippy-vue-next-ssr-demo/scripts/webpack-ssr-config/client.dev.js b/driver/js/examples/hippy-vue-next-ssr-demo/scripts/webpack-ssr-config/client.dev.js new file mode 100644 index 00000000000..f465c4ff488 --- /dev/null +++ b/driver/js/examples/hippy-vue-next-ssr-demo/scripts/webpack-ssr-config/client.dev.js @@ -0,0 +1,195 @@ +const path = require('path'); +const fs = require('fs'); +const HippyDynamicImportPlugin = require('@hippy/hippy-dynamic-import-plugin'); +const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin'); +const { VueLoaderPlugin } = require('vue-loader'); +const webpack = require('webpack'); + +const pkg = require('../../package.json'); + +let cssLoader = '@hippy/vue-css-loader'; +const hippyVueCssLoaderPath = path.resolve(__dirname, '../../../../packages/hippy-vue-css-loader/dist/css-loader.js'); +if (fs.existsSync(hippyVueCssLoaderPath)) { + console.warn(`* Using the @hippy/vue-css-loader in ${hippyVueCssLoaderPath}`); + cssLoader = hippyVueCssLoaderPath; +} else { + console.warn('* Using the @hippy/vue-css-loader defined in package.json'); +} + +/** + * webpack ssr client dev config + */ +module.exports = { + mode: 'development', + bail: true, + devtool: 'eval-source-map', + watch: true, + watchOptions: { + // file changed, rebuild delay time + aggregateTimeout: 1000, + }, + devServer: { + remote: { + protocol: 'http', + host: '127.0.0.1', + port: 38989, + }, + // support vue dev tools,default is false + vueDevtools: false, + // not support one debug server debug multiple app + multiple: false, + // ssr do not support hot replacement now + hot: false, + // default is true + liveReload: false, + client: { + // hippy do not support error tips layer + overlay: false, + }, + devMiddleware: { + // write hot replacement file to disk + writeToDisk: true, + }, + }, + entry: { + // client async bundle + home: [path.resolve(pkg.nativeMain)], + // client ssr entry + index: [path.resolve(pkg.ssrMain)], + }, + output: { + filename: '[name].bundle', + path: path.resolve('./dist/dev/'), + globalObject: '(0, eval)("this")', + }, + plugins: [ + new webpack.NamedModulesPlugin(), + new webpack.DefinePlugin({ + 'process.env': { + NODE_ENV: JSON.stringify('development'), + HOST: JSON.stringify(process.env.DEV_HOST || '127.0.0.1'), + PORT: JSON.stringify(process.env.DEV_PORT || 38989), + }, + __VUE_OPTIONS_API__: true, + __VUE_PROD_DEVTOOLS__: false, + __PLATFORM__: null, + }), + new CaseSensitivePathsPlugin(), + new VueLoaderPlugin(), + new HippyDynamicImportPlugin(), + ], + module: { + rules: [ + { + test: /\.vue$/, + use: [ + { + loader: 'vue-loader', + options: { + compilerOptions: { + // disable vue3 dom patch flag,because hippy do not support innerHTML + hoistStatic: false, + // whitespace handler, default is 'condense', it can be set 'preserve' + whitespace: 'condense', + // do not generate html comment node + comments: false, + }, + }, + }, + ], + }, + { + test: /\.(le|c)ss$/, + use: [cssLoader, 'less-loader'], + }, + { + test: /\.t|js$/, + use: [ + { + loader: 'babel-loader', + options: { + sourceType: 'unambiguous', + presets: [ + [ + '@babel/preset-env', + { + targets: { + chrome: 57, + ios: 9, + }, + }, + ], + ], + plugins: [ + ['@babel/plugin-proposal-class-properties'], + ['@babel/plugin-proposal-decorators', { legacy: true }], + ['@babel/plugin-transform-runtime', { regenerator: true }], + ], + }, + }, + ], + }, + { + test: /\.(png|jpe?g|gif)$/i, + use: [{ + loader: 'url-loader', + options: { + // if you would like to use base64 for picture, uncomment limit: true + // limit: true, + fallback: 'file-loader', + name: '[name].[ext]', + outputPath: 'assets/', + }, + }], + }, + { + test: /\.(ts)$/, + use: [ + { + loader: 'ts-loader', + options: { + transpileOnly: true, + appendTsSuffixTo: [/\.vue$/], + }, + }, + ], + exclude: /node_modules/, + }, + { + test: /\.mjs$/, + include: /node_modules/, + type: 'javascript/auto', + }, + ], + }, + resolve: { + extensions: ['.js', '.vue', '.json', '.ts'], + alias: (() => { + const aliases = { + src: path.resolve('./src'), + }; + + // If @vue/runtime-core was built exist in packages directory then make an alias + // Remove the section if you don't use it + const hippyVueRuntimeCorePath = path.resolve(__dirname, '../../../../packages/hippy-vue-next/node_modules/@vue/runtime-core'); + if (fs.existsSync(path.resolve(hippyVueRuntimeCorePath, 'index.js'))) { + console.warn(`* Using the @vue/runtime-core in ${hippyVueRuntimeCorePath} as vue alias`); + aliases['@vue/runtime-core'] = hippyVueRuntimeCorePath; + } else { + console.warn('* Using the @vue/runtime-core defined in package.json'); + } + + // If @hippy/vue-next was built exist in packages directory then make an alias + // Remove the section if you don't use it + const hippyVueNextPath = path.resolve(__dirname, '../../../../packages/hippy-vue-next/dist'); + if (fs.existsSync(path.resolve(hippyVueNextPath, 'index.js'))) { + console.warn(`* Using the @hippy/vue-next in ${hippyVueNextPath} as @hippy/vue-next alias`); + aliases['@hippy/vue-next'] = hippyVueNextPath; + } else { + console.warn('* Using the @hippy/vue-next defined in package.json'); + } + + return aliases; + })(), + }, +}; diff --git a/driver/js/examples/hippy-vue-next-ssr-demo/scripts/webpack-ssr-config/client.entry.js b/driver/js/examples/hippy-vue-next-ssr-demo/scripts/webpack-ssr-config/client.entry.js new file mode 100644 index 00000000000..f710f743a1b --- /dev/null +++ b/driver/js/examples/hippy-vue-next-ssr-demo/scripts/webpack-ssr-config/client.entry.js @@ -0,0 +1,97 @@ +const path = require('path'); +const webpack = require('webpack'); + +const pkg = require('../../package.json'); + +module.exports = { + mode: 'production', + devtool: false, + entry: { + index: path.resolve(pkg.ssrMain), + }, + output: { + filename: '[name].js', + strictModuleExceptionHandling: true, + path: path.resolve('./dist'), + globalObject: '(0, eval)("this")', + }, + plugins: [ + new webpack.DefinePlugin({ + 'process.env': { + NODE_ENV: JSON.stringify('production'), + }, + __VUE_OPTIONS_API__: true, + __VUE_PROD_DEVTOOLS__: false, + __PLATFORM__: null, + }), + ], + module: { + rules: [ + { + test: /\.t|js$/, + use: [{ + loader: 'babel-loader', + options: { + sourceType: 'unambiguous', + presets: [ + [ + '@babel/preset-env', + { + targets: { + chrome: 57, + ios: 9, + }, + }, + ], + ], + plugins: [ + ['@babel/plugin-proposal-class-properties'], + ['@babel/plugin-proposal-decorators', { legacy: true }], + ['@babel/plugin-transform-runtime', { regenerator: true }], + ], + }, + }, + ], + }, + { + test: /\.(png|jpe?g|gif)$/i, + use: [{ + loader: 'url-loader', + options: { + // comment line when production environment + // entry file do not have image asset + limit: true, + // limit: 8192, + // fallback: 'file-loader', + // name: '[name].[ext]', + // outputPath: 'assets/', + }, + }], + }, + { + test: /\.(ts)$/, + use: [ + { + loader: 'ts-loader', + options: { + transpileOnly: true, + appendTsSuffixTo: [/\.vue$/], + }, + }, + ], + exclude: /node_modules/, + }, + { + test: /\.mjs$/, + include: /node_modules/, + type: 'javascript/auto', + }, + ], + }, + resolve: { + extensions: ['.js', '.json', '.ts'], + alias: (() => ({ + src: path.resolve('./src'), + }))(), + }, +}; diff --git a/driver/js/examples/hippy-vue-next-ssr-demo/scripts/webpack-ssr-config/client.ios.js b/driver/js/examples/hippy-vue-next-ssr-demo/scripts/webpack-ssr-config/client.ios.js new file mode 100644 index 00000000000..eec18701059 --- /dev/null +++ b/driver/js/examples/hippy-vue-next-ssr-demo/scripts/webpack-ssr-config/client.ios.js @@ -0,0 +1,3 @@ +const { getWebpackSsrBaseConfig } = require('./client.base'); + +module.exports = getWebpackSsrBaseConfig('ios', 'production'); diff --git a/driver/js/examples/hippy-vue-next-ssr-demo/scripts/webpack-ssr-config/client.ios.vendor.js b/driver/js/examples/hippy-vue-next-ssr-demo/scripts/webpack-ssr-config/client.ios.vendor.js new file mode 100644 index 00000000000..6783d5a86ba --- /dev/null +++ b/driver/js/examples/hippy-vue-next-ssr-demo/scripts/webpack-ssr-config/client.ios.vendor.js @@ -0,0 +1,108 @@ +const fs = require('fs'); +const path = require('path'); +const webpack = require('webpack'); +const { VueLoaderPlugin } = require('vue-loader'); +const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin'); + +const platform = 'ios'; + +module.exports = { + mode: 'production', + bail: true, + entry: { + vendor: [path.resolve(__dirname, '../vendor.js')], + }, + output: { + filename: `[name].${platform}.js`, + path: path.resolve(`./dist/${platform}/`), + globalObject: '(0, eval)("this")', + library: 'hippyVueBase', + }, + plugins: [ + new webpack.NamedModulesPlugin(), + new webpack.DefinePlugin({ + 'process.env.NODE_ENV': JSON.stringify('production'), + __PLATFORM__: JSON.stringify(platform), + }), + new CaseSensitivePathsPlugin(), + new VueLoaderPlugin(), + new webpack.DllPlugin({ + context: path.resolve(__dirname, '../..'), + path: path.resolve(__dirname, `../../dist/${platform}/[name]-manifest.json`), + name: 'hippyVueBase', + }), + ], + module: { + rules: [ + { + test: /\.vue$/, + use: [ + { + loader: 'vue-loader', + options: { + compilerOptions: { + // disable vue3 dom patch flag,because hippy do not support innerHTML + hoistStatic: false, + // whitespace handler, default is 'condense', it can be set 'preserve' + whitespace: 'condense', + }, + }, + }, + ], + }, + { + test: /\.(js)$/, + use: [ + { + loader: 'babel-loader', + options: { + presets: [ + [ + '@babel/preset-env', + { + targets: { + ios: 9, + }, + }, + ], + ], + plugins: [ + ['@babel/plugin-proposal-class-properties'], + ], + }, + }, + ], + }, + ], + }, + resolve: { + extensions: ['.js', '.vue', '.json', '.ts'], + alias: (() => { + const aliases = { + src: path.resolve('./src'), + }; + + // If @vue/runtime-core was built exist in packages directory then make an alias + // Remove the section if you don't use it + const hippyVueRuntimeCorePath = path.resolve(__dirname, '../../../../packages/hippy-vue-next/node_modules/@vue/runtime-core'); + if (fs.existsSync(path.resolve(hippyVueRuntimeCorePath, 'index.js'))) { + console.warn(`* Using the @vue/runtime-core in ${hippyVueRuntimeCorePath} as vue alias`); + aliases['@vue/runtime-core'] = hippyVueRuntimeCorePath; + } else { + console.warn('* Using the @vue/runtime-core defined in package.json'); + } + + // If @hippy/vue-next was built exist in packages directory then make an alias + // Remove the section if you don't use it + const hippyVueNextPath = path.resolve(__dirname, '../../../../packages/hippy-vue-next/dist'); + if (fs.existsSync(path.resolve(hippyVueNextPath, 'index.js'))) { + console.warn(`* Using the @hippy/vue-next in ${hippyVueNextPath} as @hippy/vue-next alias`); + aliases['@hippy/vue-next'] = hippyVueNextPath; + } else { + console.warn('* Using the @hippy/vue-next defined in package.json'); + } + + return aliases; + })(), + }, +}; diff --git a/driver/js/examples/hippy-vue-next-ssr-demo/scripts/webpack-ssr-config/server.dev.js b/driver/js/examples/hippy-vue-next-ssr-demo/scripts/webpack-ssr-config/server.dev.js new file mode 100644 index 00000000000..e82008654ed --- /dev/null +++ b/driver/js/examples/hippy-vue-next-ssr-demo/scripts/webpack-ssr-config/server.dev.js @@ -0,0 +1,186 @@ +const path = require('path'); +const fs = require('fs'); +const webpack = require('webpack'); + +const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin'); +const compilerSSR = require('@hippy/vue-next-compiler-ssr'); +const { VueLoaderPlugin } = require('vue-loader'); +const pkg = require('../../package.json'); + +let cssLoader = '@hippy/vue-css-loader'; +const hippyVueCssLoaderPath = path.resolve(__dirname, '../../../../packages/hippy-vue-css-loader/dist/css-loader.js'); +if (fs.existsSync(hippyVueCssLoaderPath)) { + console.warn(`* Using the @hippy/vue-css-loader in ${hippyVueCssLoaderPath}`); + cssLoader = hippyVueCssLoaderPath; +} else { + console.warn('* Using the @hippy/vue-css-loader defined in package.json'); +} + +let vueNext = '@hippy/vue-next'; +const hippyVueNextPath = path.resolve(__dirname, '../../../../packages/hippy-vue-next/dist/index.js'); +if (fs.existsSync(hippyVueNextPath)) { + console.warn(`* Using the @hippy/vue-next in ${hippyVueNextPath}`); + vueNext = hippyVueNextPath; +} else { + console.warn('* Using the @hippy/vue-next defined in package.json'); +} +const { isNativeTag } = require(vueNext); + + +module.exports = { + mode: 'development', + bail: true, + devtool: 'source-map', + target: 'node', + watch: true, + watchOptions: { + // file changed, rebuild delay time + aggregateTimeout: 1000, + }, + entry: { + index: path.resolve(pkg.serverEntry), + }, + output: { + filename: 'index.js', + strictModuleExceptionHandling: true, + path: path.resolve('dist/server'), + }, + plugins: [ + // only generate one chunk at server side + new webpack.optimize.LimitChunkCountPlugin({ + maxChunks: 1, + }), + new webpack.DefinePlugin({ + 'process.env': { + NODE_ENV: JSON.stringify('development'), + HIPPY_SSR: true, + }, + __VUE_OPTIONS_API__: true, + __VUE_PROD_DEVTOOLS__: false, + }), + new CaseSensitivePathsPlugin(), + new VueLoaderPlugin(), + ], + module: { + rules: [ + { + test: /\.vue$/, + use: [ + { + loader: 'vue-loader', + options: { + compilerOptions: { + // because hippy do not support innerHTML, so we should close this feature + hoistStatic: false, + // whitespace handler, default is 'condense', it can be set 'preserve' + whitespace: 'condense', + // Vue will recognize non-HTML tags as components, so for Hippy native tags, + // Vue needs to be informed to render them as custom elements + isCustomElement: tag => isNativeTag && isNativeTag(tag), + // real used ssr runtime package, render vue node at server side + ssrRuntimeModuleName: '@hippy/vue-next-server-renderer', + // do not generate html comment node + comments: false, + }, + // real used vue compiler + compiler: compilerSSR, + }, + }, + ], + }, + { + test: /\.(le|c)ss$/, + use: [cssLoader, 'less-loader'], + }, + { + test: /\.t|js$/, + use: [ + { + loader: 'babel-loader', + options: { + sourceType: 'unambiguous', + presets: [ + [ + '@babel/preset-env', + { + targets: { + node: '16.0', + }, + }, + ], + ], + plugins: [ + ['@babel/plugin-proposal-nullish-coalescing-operator'], + ], + }, + }, + ], + }, + { + test: /\.(png|jpe?g|gif)$/i, + use: [{ + loader: 'url-loader', + options: { + // if you would like to use base64 for picture, uncomment limit: true + // limit: true, + limit: true, + fallback: 'file-loader', + name: '[name].[ext]', + outputPath: 'assets/', + }, + }], + }, + { + test: /\.(ts)$/, + use: [ + { + loader: 'ts-loader', + options: { + transpileOnly: true, + appendTsSuffixTo: [/\.vue$/], + }, + }, + ], + exclude: /node_modules/, + }, + { + test: /\.mjs$/, + include: /node_modules/, + type: 'javascript/auto', + }, + ], + }, + resolve: { + extensions: ['.js', '.vue', '.json', '.ts'], + alias: (() => { + const aliases = { + src: path.resolve('./src'), + }; + + // If @vue/runtime-core was built exist in packages directory then make an alias + // Remove the section if you don't use it + const hippyVueRuntimeCorePath = path.resolve(__dirname, '../../../../packages/hippy-vue-next/node_modules/@vue/runtime-core'); + if (fs.existsSync(path.resolve(hippyVueRuntimeCorePath, 'index.js'))) { + console.warn(`* Using the @vue/runtime-core in ${hippyVueRuntimeCorePath} as vue alias`); + aliases['@vue/runtime-core'] = hippyVueRuntimeCorePath; + } else { + console.warn('* Using the @vue/runtime-core defined in package.json'); + } + + // If @hippy/vue-next was built exist in packages directory then make an alias + // Remove the section if you don't use it + const hippyVueNextPath = path.resolve(__dirname, '../../../../packages/hippy-vue-next/dist'); + if (fs.existsSync(path.resolve(hippyVueNextPath, 'index.js'))) { + console.warn(`* Using the @hippy/vue-next in ${hippyVueNextPath} as @hippy/vue-next alias`); + aliases['@hippy/vue-next'] = hippyVueNextPath; + } else { + console.warn('* Using the @hippy/vue-next defined in package.json'); + } + + return aliases; + })(), + }, + externals: { + express: 'commonjs express', // this line is just to use the express dependency in a commonjs way + }, +}; diff --git a/driver/js/examples/hippy-vue-next-ssr-demo/scripts/webpack-ssr-config/server.entry.js b/driver/js/examples/hippy-vue-next-ssr-demo/scripts/webpack-ssr-config/server.entry.js new file mode 100644 index 00000000000..3ecee4aea93 --- /dev/null +++ b/driver/js/examples/hippy-vue-next-ssr-demo/scripts/webpack-ssr-config/server.entry.js @@ -0,0 +1,177 @@ +const path = require('path'); +const fs = require('fs'); +const webpack = require('webpack'); + +const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin'); +const compilerSSR = require('@hippy/vue-next-compiler-ssr'); +const { VueLoaderPlugin } = require('vue-loader'); +const pkg = require('../../package.json'); + +let cssLoader = '@hippy/vue-css-loader'; +const hippyVueCssLoaderPath = path.resolve(__dirname, '../../../../packages/hippy-vue-css-loader/dist/css-loader.js'); +if (fs.existsSync(hippyVueCssLoaderPath)) { + console.warn(`* Using the @hippy/vue-css-loader in ${hippyVueCssLoaderPath}`); + cssLoader = hippyVueCssLoaderPath; +} else { + console.warn('* Using the @hippy/vue-css-loader defined in package.json'); +} + +let vueNext = '@hippy/vue-next'; +const hippyVueNextPath = path.resolve(__dirname, '../../../../packages/hippy-vue-next/dist/index.js'); +if (fs.existsSync(hippyVueNextPath)) { + console.warn(`* Using the @hippy/vue-next in ${hippyVueNextPath}`); + vueNext = hippyVueNextPath; +} else { + console.warn('* Using the @hippy/vue-next defined in package.json'); +} +const { isNativeTag } = require(vueNext); + +module.exports = { + mode: 'production', + bail: true, + devtool: false, + target: 'node', + entry: { + index: path.resolve(pkg.serverEntry), + }, + output: { + filename: 'index.js', + strictModuleExceptionHandling: true, + path: path.resolve('dist/server'), + }, + plugins: [ + // only generate one chunk at server side + new webpack.optimize.LimitChunkCountPlugin({ + maxChunks: 1, + }), + new webpack.DefinePlugin({ + 'process.env': { + NODE_ENV: JSON.stringify('production'), + HIPPY_SSR: true, + }, + __VUE_OPTIONS_API__: true, + __VUE_PROD_DEVTOOLS__: false, + }), + new CaseSensitivePathsPlugin(), + new VueLoaderPlugin(), + ], + module: { + rules: [ + { + test: /\.vue$/, + use: [ + { + loader: 'vue-loader', + options: { + compilerOptions: { + // because hippy do not support innerHTML, so we should close this feature + hoistStatic: false, + // whitespace handler, default is 'condense', it can be set 'preserve' + whitespace: 'condense', + // Vue will recognize non-HTML tags as components, so for Hippy native tags, + // Vue needs to be informed to render them as custom elements + isCustomElement: tag => isNativeTag && isNativeTag(tag), + // real used ssr runtime package, render vue node at server side + ssrRuntimeModuleName: '@hippy/vue-next-server-renderer', + // do not generate html comment node + comments: false, + }, + // real used vue compiler + compiler: compilerSSR, + }, + }, + ], + }, + { + test: /\.(le|c)ss$/, + use: [cssLoader, 'less-loader'], + }, + { + test: /\.t|js$/, + use: [ + { + loader: 'babel-loader', + options: { + sourceType: 'unambiguous', + presets: [ + [ + '@babel/preset-env', + { + targets: { + node: '16.0', + }, + }, + ], + ], + plugins: [ + ['@babel/plugin-proposal-nullish-coalescing-operator'], + ], + }, + }, + ], + }, + { + test: /\.(png|jpe?g|gif)$/i, + use: [{ + loader: 'url-loader', + options: { + // if you would like to use base64 for picture, uncomment limit: true + // limit: true, + limit: 8192, + fallback: 'file-loader', + name: '[name].[ext]', + outputPath: 'assets/', + }, + }], + }, + { + test: /\.(ts)$/, + use: [ + { + loader: 'ts-loader', + options: { + transpileOnly: true, + appendTsSuffixTo: [/\.vue$/], + }, + }, + ], + exclude: /node_modules/, + }, + { + test: /\.mjs$/, + include: /node_modules/, + type: 'javascript/auto', + }, + ], + }, + resolve: { + extensions: ['.js', '.vue', '.json', '.ts'], + alias: (() => { + const aliases = { + src: path.resolve('./src'), + }; + + // If @vue/runtime-core was built exist in packages directory then make an alias + // Remove the section if you don't use it + const hippyVueRuntimeCorePath = path.resolve(__dirname, '../../../../packages/hippy-vue-next/node_modules/@vue/runtime-core'); + if (fs.existsSync(path.resolve(hippyVueRuntimeCorePath, 'index.js'))) { + console.warn(`* Using the @vue/runtime-core in ${hippyVueRuntimeCorePath} as vue alias`); + aliases['@vue/runtime-core'] = hippyVueRuntimeCorePath; + } else { + console.warn('* Using the @vue/runtime-core defined in package.json'); + } + + // If @hippy/vue-next was built exist in packages directory then make an alias + // Remove the section if you don't use it + const hippyVueNextPath = path.resolve(__dirname, '../../../../packages/hippy-vue-next/dist'); + if (fs.existsSync(path.resolve(hippyVueNextPath, 'index.js'))) { + console.warn(`* Using the @hippy/vue-next in ${hippyVueNextPath} as @hippy/vue-next alias`); + aliases['@hippy/vue-next'] = hippyVueNextPath; + } else { + console.warn('* Using the @hippy/vue-next defined in package.json'); + } + + return aliases; + })(), + }, +}; diff --git a/driver/js/examples/hippy-vue-next-ssr-demo/scripts/webpack.ssr.build.js b/driver/js/examples/hippy-vue-next-ssr-demo/scripts/webpack.ssr.build.js new file mode 100644 index 00000000000..fe0210af821 --- /dev/null +++ b/driver/js/examples/hippy-vue-next-ssr-demo/scripts/webpack.ssr.build.js @@ -0,0 +1,109 @@ +/** + * build js script for ssr production + */ +const { arch } = require('os'); +const { exec, rm, cp } = require('shelljs'); + +let envPrefixStr = 'cross-env-os os="Windows_NT,Linux,Darwin" minVersion=17 NODE_OPTIONS=--openssl-legacy-provider'; +const isArmCpu = arch() + .toLowerCase() + .includes('arm'); +if (isArmCpu) { + envPrefixStr = ''; +} + +/** + * get executed script + * + * @param configFile - config file name + */ +function getScriptCommand(configFile) { + return `${envPrefixStr} webpack --config scripts/webpack-ssr-config/${configFile} --mode production`; +} + +/** + * execute script + * + * @param scriptStr - script content + * @param options - shelljs options + */ +function runScript(scriptStr, options = { silent: false }) { + const result = exec(scriptStr, options); + if (result.code !== 0) { + console.error(`❌ execute cmd - "${scriptStr}" error: ${result.stderr}`); + process.exit(1); + } +} + +/** + * build ssr client entry bundle + */ +function buildServerEntry() { + // build server entry + runScript(getScriptCommand('server.entry.js')); +} + +/** + * build ssr sever and client bundle + */ +function buildJsBundle() { + // build Android client bundle + runScript(getScriptCommand('client.android.js')); + // build iOS client bundle + runScript(getScriptCommand('client.ios.js')); + // 3. build client entry + runScript(getScriptCommand('client.entry.js')); +} + +/** + * build js vendor for production + */ +function buildJsVendor() { + // ios + runScript(getScriptCommand('client.ios.vendor.js')); + // android + runScript(getScriptCommand('client.android.vendor.js')); +} + +/** + * generate client entry js bundle for production + */ +function generateClientEntryForProduction() { + // copy js entry to every platform + // ios + cp('-f', './dist/index.js', './dist/ios/index.ios.js'); + // android + cp('-f', './dist/index.js', './dist/android/index.android.js'); +} + +/** + * copy generated files to native demo + */ +function copyFilesToNativeDemo() { + cp('-Rf', './dist/ios/*', '../ios-demo/res/'); // Update the ios demo project + cp('-Rf', './dist/android/*', '../android-demo/res/'); // # Update the android project +} + +/** + * build production bundle + */ +function buildProduction() { + // production, build all entry bundle, ssr server should execute by user + // first, remove dist directory + rm('-rf', './dist'); + // second, build js vendor + buildJsVendor(); + // third, build all js bundle + buildJsBundle(); + // fourth, build client entry + buildServerEntry(); + // fifth, build every platform's client entry + generateClientEntryForProduction(); + // last, copy all files to native demo + copyFilesToNativeDemo(); +} + +// build production bundle +buildProduction(); + + diff --git a/driver/js/examples/hippy-vue-next-ssr-demo/scripts/webpack.ssr.dev.js b/driver/js/examples/hippy-vue-next-ssr-demo/scripts/webpack.ssr.dev.js new file mode 100644 index 00000000000..4c225478b6f --- /dev/null +++ b/driver/js/examples/hippy-vue-next-ssr-demo/scripts/webpack.ssr.dev.js @@ -0,0 +1,39 @@ +/** + * build script for ssr + */ + +const webpack = require('webpack'); +const { exec } = require('shelljs'); +const serverConfig = require('./webpack-ssr-config/server.dev'); + +const compiler = webpack(serverConfig); +let childProcess = null; + +/** + * execute script + * + * @param scriptStr - script content + * @param options - shelljs options + */ +function runScript(scriptStr, options) { + if (childProcess) { + // kill process first + childProcess.kill(); + } + childProcess = exec(scriptStr, options, (code, stdout, stderr) => { + if (code) { + console.error(`❌ execute cmd - "${scriptStr}" error: ${stderr}`); + process.exit(1); + } + }); +} + +compiler.hooks.done.tap('DonePlugin', () => { + // restart node process after build success + setTimeout(() => { + runScript('node ./dist/server/index.js', { async: true, silent: false }); + }, 0); +}); + +// watch server entry change +compiler.watch({}, () => {}); diff --git a/driver/js/examples/hippy-vue-next-ssr-demo/server.ts b/driver/js/examples/hippy-vue-next-ssr-demo/server.ts new file mode 100644 index 00000000000..db1660b26f5 --- /dev/null +++ b/driver/js/examples/hippy-vue-next-ssr-demo/server.ts @@ -0,0 +1,80 @@ +import express from 'express'; +import { render, HIPPY_GLOBAL_STYLE_NAME } from 'src/main-server'; + +interface MinifiedStyleDeclaration { + [key: number]: number | string; +} + +/** + * minify css content + */ +function minifyStyleContent(rawStyleContent): NeedToTyped[] | MinifiedStyleDeclaration[] { + if (rawStyleContent?.length && Array.isArray(rawStyleContent)) { + const minifiedStyle: MinifiedStyleDeclaration[] = []; + rawStyleContent.forEach((styleContent) => { + // minified style is array, 0 index is selectors, 1 index is declaration, no hash + minifiedStyle.push([ + styleContent.selectors, + // minify declarations + styleContent.declarations.map(declaration => [declaration.property, declaration.value]), + ]); + }); + return minifiedStyle; + } + + return rawStyleContent; +} + +/** + * get ssr style content + * + * @param globalStyleName - hippy global style name + */ +function getSsrStyleContent(globalStyleName): NeedToTyped[] { + if (global.ssrStyleContentList) { + return global.ssrStyleContentList; + } + // cache global style sheet, then non first request could return directly, unnecessary to + // serialize again + global.ssrStyleContentList = JSON.stringify(minifyStyleContent(global[globalStyleName])); + + return global.ssrStyleContentList; +} + +// server listen port +const serverPort = 8080; +// init http server +const server = express(); +// use json middleware +server.use(express.json()); + +// listen request +server.all('/getSsrFirstScreenData', (req, rsp) => { + // get hippy ssr node list and other const + render('/', { + appName: 'Demo', + }, req.body).then(({ + list, + store, + uniqueId, + }) => { + // send response + rsp.json({ + code: 0, + data: list, + store: store.state.value, + uniqueId, + styleContent: getSsrStyleContent(HIPPY_GLOBAL_STYLE_NAME), + }); + }) + .catch((error) => { + rsp.json({ + code: -1, + message: `get ssr data error: ${JSON.stringify(error)}`, + }); + }); +}); + +// start server +server.listen(serverPort); +console.log(`Server listen on:${serverPort}`); diff --git a/driver/js/examples/hippy-vue-next-ssr-demo/tsconfig.json b/driver/js/examples/hippy-vue-next-ssr-demo/tsconfig.json new file mode 100644 index 00000000000..c69a49e317f --- /dev/null +++ b/driver/js/examples/hippy-vue-next-ssr-demo/tsconfig.json @@ -0,0 +1,35 @@ +{ + "compilerOptions": { + "strict": true, + "target": "esnext", + "module": "esnext", + "lib": [ + "esnext", + "dom" + ], + "declaration": true, + "rootDir": ".", + "baseUrl": "./", + "outDir": "dist", + "jsx": "preserve", + "moduleResolution": "node", + "allowJs": false, + "esModuleInterop": true, + "noImplicitAny": false + }, + "include": [ + "src/*.ts", + "src/*.vue", + "src/**/*.ts", + "src/**/*.vue", + "test", + "app.d.ts", + "server.ts" + ], + "exclude": [ + "node_modules", + "dist", + "**/dist", + "**/node_modules" + ] +} diff --git a/driver/js/package.json b/driver/js/package.json index 34f52399fe8..5483b3a8a95 100644 --- a/driver/js/package.json +++ b/driver/js/package.json @@ -95,7 +95,10 @@ }, "jest": { "projects": [ - "/packages/hippy-vue-next" + "/packages/hippy-vue-next", + "/packages/hippy-vue-next-style-parser", + "/packages/hippy-vue-next-server-renderer", + "/packages/hippy-vue-next-compiler-ssr" ] } } diff --git a/driver/js/packages/hippy-vue-next-compiler-ssr/README.md b/driver/js/packages/hippy-vue-next-compiler-ssr/README.md new file mode 100644 index 00000000000..56e67cab020 --- /dev/null +++ b/driver/js/packages/hippy-vue-next-compiler-ssr/README.md @@ -0,0 +1,6 @@ +# @hippy/vue-next-compiler-ssr + +### Introduction + +This package is provided server renderer compiler function for @hippy/vue-next + diff --git a/driver/js/packages/hippy-vue-next-compiler-ssr/__test__/component.test.ts b/driver/js/packages/hippy-vue-next-compiler-ssr/__test__/component.test.ts new file mode 100644 index 00000000000..81937735177 --- /dev/null +++ b/driver/js/packages/hippy-vue-next-compiler-ssr/__test__/component.test.ts @@ -0,0 +1,132 @@ +/* + * Tencent is pleased to support the open source community by making + * Hippy available. + * + * Copyright (C) 2017-2022 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { compile } from '../src'; +import { getSsrRenderFunctionBody } from './utils'; + +/** + * component unit test case + */ +describe('component.test.ts', () => { + describe('component should compile correct', () => { + it('base component compile', () => { + const { code } = compile(''); + expect(getSsrRenderFunctionBody(code)).toEqual('const _component_foo = _resolveComponent("foo") _push(_ssrRenderComponent(_component_foo,_attrs,null,_parent))'); + }); + it('custom tag should compile correct', () => { + const { code } = compile('', { + isCustomElement: tag => tag === 'foo', + }); + expect(getSsrRenderFunctionBody(code)).toEqual('_push(`{"id":${_ssrGetUniqueId()},"index":0,"name":"foo","tagName":"foo","props":{"mergedProps":${JSON.stringify(_attrs)},},"children":[]},`)'); + }); + it('component compile props', () => { + const { code } = compile(''); + expect(getSsrRenderFunctionBody(code)).toEqual('const _component_foo = _resolveComponent("foo") _push(_ssrRenderComponent(_component_foo,_mergeProps({id:"a",prop:_ctx.b},_attrs),null,_parent))'); + }); + it('component compile event listeners', () => { + const { code } = compile(''); + expect(getSsrRenderFunctionBody(code)).toEqual('const _component_foo = _resolveComponent("foo") _push(_ssrRenderComponent(_component_foo,_mergeProps({onClick:_ctx.bar},_attrs),null,_parent))'); + }); + it('dynamic component compile', () => { + let { code } = compile(''); + expect(getSsrRenderFunctionBody(code)).toEqual('_ssrRenderVNode(_push,_createVNode(_resolveDynamicComponent("foo"),_mergeProps({prop:_ctx.b},_attrs),null),_parent)'); + code = compile('').code; + expect(getSsrRenderFunctionBody(code)).toEqual('_ssrRenderVNode(_push,_createVNode(_resolveDynamicComponent(_ctx.foo),_mergeProps({prop:_ctx.b},_attrs),null),_parent)'); + }); + it('v-if component compile', () => { + const { code } = compile(''); + expect(getSsrRenderFunctionBody(code)).toEqual('const _component_foo = _resolveComponent("foo") if (_ctx.ok) {_push(_ssrRenderComponent(_component_foo,_attrs,null,_parent))} else {_push(`{"id":-1,"name":"comment","props":{"text":""}},`)}'); + }); + it('v-for component compile', () => { + const { code } = compile(''); + expect(getSsrRenderFunctionBody(code)).toEqual('const _component_foo = _resolveComponent("foo") _push(`{"id":-1,"name":"comment","props":{"text":"["}},`) _ssrRenderList(_ctx.names,(key) => {_push(_ssrRenderComponent(_component_foo,{key:key},null,_parent))}) _push(`{"id":-1,"name":"comment","props":{"text":"]"}},`)'); + }); + it('nested component compile', () => { + const { code } = compile(''); + expect(getSsrRenderFunctionBody(code)).toEqual('const _component_foo = _resolveComponent("foo") const _component_bar = _resolveComponent("bar") _push(_ssrRenderComponent(_component_foo,_attrs,{default:_withCtx((_,_push,_parent,_scopeId) => {if (_push) {_push(_ssrRenderComponent(_component_bar,null,null,_parent,_scopeId))} else {return [ _createVNode(_component_bar) ]}}),_:1 /* STABLE */},_parent))'); + }); + }); + describe('component compile with slot', () => { + it('default slot', () => { + const { code } = compile('
'); + expect(getSsrRenderFunctionBody(code)).toEqual('const _component_foo = _resolveComponent("foo") _push(_ssrRenderComponent(_component_foo,_attrs,{default:_withCtx((_,_push,_parent,_scopeId) => {if (_push) {_push(`{"id":${_ssrGetUniqueId()},"index":0,"name":"View","tagName":"div","props":{},${_scopeId}"children":[]},`)} else {return [ _createVNode("div") ]}}),_:1 /* STABLE */},_parent))'); + }); + it('named slot', () => { + const { code } = compile(''); + expect(getSsrRenderFunctionBody(code)).toEqual('const _component_foo = _resolveComponent("foo") _push(_ssrRenderComponent(_component_foo,_attrs,{named:_withCtx((_,_push,_parent,_scopeId) => {if (_push) {_push(`{"id":${_ssrGetUniqueId()},"index":0,"name":"View","tagName":"div","props":{},${_scopeId}"children":[]},`)} else {return [ _createVNode("div") ]}}),_:1 /* STABLE */},_parent))'); + }); + }); + describe('supported vue built-in component compile', () => { + it('keep-alive compile', () => { + const { code } = compile(''); + expect(getSsrRenderFunctionBody(code)).toEqual('const _component_foo = _resolveComponent("foo") _push(_ssrRenderComponent(_component_foo,_attrs,null,_parent))'); + }); + it('transition compile', () => { + const { code } = compile('
'); + expect(getSsrRenderFunctionBody(code)).toEqual('_push(`{"id":${_ssrGetUniqueId()},"index":0,"name":"View","tagName":"div","props":{"mergedProps":${JSON.stringify(_attrs)},},"children":[]},`)'); + }); + }); + describe('inject css vars compile', () => { + it('base inject compile', () => { + const { code } = compile('', { ssrCssVars: '{ color }' }); + expect(getSsrRenderFunctionBody(code)).toEqual('const _component_foo = _resolveComponent("foo") const _cssVars = {style:{color:_ctx.color}} _push(_ssrRenderComponent(_component_foo,_mergeProps(_attrs,_cssVars),null,_parent))'); + }); + }); + describe('scopedId compile', () => { + it('single component compile', () => { + const { code } = compile('
', { scopeId: 'data-v-12345' }); + expect(getSsrRenderFunctionBody(code)).toEqual('const _component_foo = _resolveComponent("foo") _push(_ssrRenderComponent(_component_foo,_attrs,{default:_withCtx((_,_push,_parent,_scopeId) => {if (_push) {_push(`{"id":${_ssrGetUniqueId()},"index":0,"name":"View","tagName":"div","props":{"data-v-12345":"",},${_scopeId}"children":[]},`)} else {return [ _createVNode("div") ]}}),_:1 /* STABLE */},_parent))'); + }); + }); + describe('native component compile', () => { + it('swiper compile', () => { + const { code } = compile(''); + expect((getSsrRenderFunctionBody(code))).toEqual('const _component_swiper = _resolveComponent("swiper") _push(_ssrRenderComponent(_component_swiper,_attrs,null,_parent))'); + }); + it('swiper-slide compile', () => { + const { code } = compile(''); + expect((getSsrRenderFunctionBody(code))).toEqual('const _component_swiper_slide = _resolveComponent("swiper-slide") _push(_ssrRenderComponent(_component_swiper_slide,_attrs,null,_parent))'); + }); + it('pull-header compile', () => { + const { code } = compile(''); + expect((getSsrRenderFunctionBody(code))).toEqual('const _component_pull_header = _resolveComponent("pull-header") _push(_ssrRenderComponent(_component_pull_header,_attrs,null,_parent))'); + }); + it('pull-footer compile', () => { + const { code } = compile(''); + expect((getSsrRenderFunctionBody(code))).toEqual('const _component_pull_footer = _resolveComponent("pull-footer") _push(_ssrRenderComponent(_component_pull_footer,_attrs,null,_parent))'); + }); + it('waterfall compile', () => { + const { code } = compile(''); + expect((getSsrRenderFunctionBody(code))).toEqual('const _component_waterfall = _resolveComponent("waterfall") _push(_ssrRenderComponent(_component_waterfall,_attrs,null,_parent))'); + }); + it('waterfall-item compile', () => { + const { code } = compile(''); + expect((getSsrRenderFunctionBody(code))).toEqual('const _component_waterfall_item = _resolveComponent("waterfall-item") _push(_ssrRenderComponent(_component_waterfall_item,_attrs,null,_parent))'); + }); + it('ul-refresh-wrapper compile', () => { + const { code } = compile(''); + expect((getSsrRenderFunctionBody(code))).toEqual('const _component_ul_refresh_wrapper = _resolveComponent("ul-refresh-wrapper") _push(_ssrRenderComponent(_component_ul_refresh_wrapper,_attrs,null,_parent))'); + }); + it('ul-refresh compile', () => { + const { code } = compile(''); + expect((getSsrRenderFunctionBody(code))).toEqual('const _component_ul_refresh = _resolveComponent("ul-refresh") _push(_ssrRenderComponent(_component_ul_refresh,_attrs,null,_parent))'); + }); + }); +}); diff --git a/driver/js/packages/hippy-vue-next-compiler-ssr/__test__/element.test.ts b/driver/js/packages/hippy-vue-next-compiler-ssr/__test__/element.test.ts new file mode 100644 index 00000000000..31792315281 --- /dev/null +++ b/driver/js/packages/hippy-vue-next-compiler-ssr/__test__/element.test.ts @@ -0,0 +1,245 @@ +/* + * Tencent is pleased to support the open source community by making + * Hippy available. + * + * Copyright (C) 2017-2022 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { compile } from '../src'; +import { getSsrRenderFunctionBody } from './utils'; + +/** + * element unit test case + */ +describe('element.test.ts', () => { + describe('tag should compile correct', () => { + it('div should compile correct', () => { + const { code } = compile('
'); + expect(getSsrRenderFunctionBody(code)) + .toEqual('_push(`{"id":${_ssrGetUniqueId()},"index":0,"name":"View","tagName":"div","props":{"mergedProps":${JSON.stringify(_attrs)},},"children":[]},`)'); + }); + it('button should compile correct', () => { + const { code } = compile(''); + expect(getSsrRenderFunctionBody(code)) + .toEqual('_push(`{"id":${_ssrGetUniqueId()},"index":0,"name":"View","tagName":"button","props":{"mergedProps":${JSON.stringify(_attrs)},},"children":[]},`)'); + }); + it('form should compile correct', () => { + const { code } = compile('
'); + expect(getSsrRenderFunctionBody(code)) + .toEqual('_push(`{"id":${_ssrGetUniqueId()},"index":0,"name":"View","tagName":"form","props":{"mergedProps":${JSON.stringify(_attrs)},},"children":[]},`)'); + }); + it('img should compile correct', () => { + const { code } = compile(''); + expect(getSsrRenderFunctionBody(code)) + .toEqual('_push(`{"id":${_ssrGetUniqueId()},"index":0,"name":"Image","tagName":"img","props":{"mergedProps":${JSON.stringify(_attrs)},},"children":[]},`)'); + }); + it('ul should compile correct', () => { + const { code } = compile('
    '); + expect(getSsrRenderFunctionBody(code)) + .toEqual('_push(`{"id":${_ssrGetUniqueId()},"index":0,"name":"ListView","tagName":"ul","props":{"mergedProps":${JSON.stringify(_attrs)},},"children":[]},`)'); + }); + it('li should compile correct', () => { + const { code } = compile('
  • '); + expect(getSsrRenderFunctionBody(code)) + .toEqual('_push(`{"id":${_ssrGetUniqueId()},"index":0,"name":"ListViewItem","tagName":"li","props":{"mergedProps":${JSON.stringify(_attrs)},},"children":[]},`)'); + }); + it('span should compile correct', () => { + const { code } = compile(''); + expect(getSsrRenderFunctionBody(code)) + .toEqual('_push(`{"id":${_ssrGetUniqueId()},"index":0,"name":"Text","tagName":"span","props":{"mergedProps":${JSON.stringify(_attrs)},},"children":[]},`)'); + }); + it('label should compile correct', () => { + const { code } = compile(''); + expect(getSsrRenderFunctionBody(code)) + .toEqual('_push(`{"id":${_ssrGetUniqueId()},"index":0,"name":"Text","tagName":"label","props":{"mergedProps":${JSON.stringify(_attrs)},},"children":[]},`)'); + }); + it('p should compile correct', () => { + const { code } = compile('

    '); + expect(getSsrRenderFunctionBody(code)) + .toEqual('_push(`{"id":${_ssrGetUniqueId()},"index":0,"name":"Text","tagName":"p","props":{"mergedProps":${JSON.stringify(_attrs)},},"children":[]},`)'); + }); + it('a should compile correct', () => { + const { code } = compile(''); + expect(getSsrRenderFunctionBody(code)) + .toEqual('_push(`{"id":${_ssrGetUniqueId()},"index":0,"name":"Text","tagName":"a","props":{"mergedProps":${JSON.stringify(_attrs)},},"children":[]},`)'); + }); + it('input should compile correct', () => { + const { code } = compile(''); + expect(getSsrRenderFunctionBody(code)) + .toEqual('_push(`{"id":${_ssrGetUniqueId()},"index":0,"name":"TextInput","tagName":"input","props":{"mergedProps":${JSON.stringify(_attrs)},},"children":[]},`)'); + }); + it('textarea should compile correct', () => { + const { code } = compile(''); + expect(getSsrRenderFunctionBody(code)) + .toEqual('_push(`{"id":${_ssrGetUniqueId()},"index":0,"name":"TextInput","tagName":"textarea","props":{"mergedProps":${JSON.stringify(_attrs)},},"children":[]},`)'); + }); + it('iframe should compile correct', () => { + const { code } = compile(''); + expect(getSsrRenderFunctionBody(code)) + .toEqual('_push(`{"id":${_ssrGetUniqueId()},"index":0,"name":"WebView","tagName":"iframe","props":{"mergedProps":${JSON.stringify(_attrs)},},"children":[]},`)'); + }); + it('hi-swiper should compile correct', () => { + const { code } = compile('', { isCustomElement: () => true }); + expect(getSsrRenderFunctionBody(code)) + .toEqual('_push(`{"id":${_ssrGetUniqueId()},"index":0,"name":"ViewPager","tagName":"hi-swiper","props":{"mergedProps":${JSON.stringify(_attrs)},},"children":[]},`)'); + }); + it('hi-swiper-slide should compile correct', () => { + const { code } = compile('', { isCustomElement: () => true }); + expect(getSsrRenderFunctionBody(code)) + .toEqual('_push(`{"id":${_ssrGetUniqueId()},"index":0,"name":"ViewPagerItem","tagName":"hi-swiper-slide","props":{"mergedProps":${JSON.stringify(_attrs)},},"children":[]},`)'); + }); + it('pull-header should compile correct', () => { + const { code } = compile('', { isCustomElement: () => true }); + expect(getSsrRenderFunctionBody(code)) + .toEqual('_push(`{"id":${_ssrGetUniqueId()},"index":0,"name":"PullHeaderView","tagName":"hi-pull-header","props":{"mergedProps":${JSON.stringify(_attrs)},},"children":[]},`)'); + }); + it('hi-pull-footer should compile correct', () => { + const { code } = compile('', { isCustomElement: () => true }); + expect(getSsrRenderFunctionBody(code)) + .toEqual('_push(`{"id":${_ssrGetUniqueId()},"index":0,"name":"PullFooterView","tagName":"hi-pull-footer","props":{"mergedProps":${JSON.stringify(_attrs)},},"children":[]},`)'); + }); + it('dialog should compile correct', () => { + const { code } = compile('', { isCustomElement: () => true }); + expect(getSsrRenderFunctionBody(code)) + .toEqual('_push(`{"id":${_ssrGetUniqueId()},"index":0,"name":"Modal","tagName":"dialog","props":{"mergedProps":${JSON.stringify(_attrs)},},"children":[]},`)'); + }); + it('hi-ul-refresh-wrapper should compile correct', () => { + const { code } = compile('', { isCustomElement: () => true }); + expect(getSsrRenderFunctionBody(code)) + .toEqual('_push(`{"id":${_ssrGetUniqueId()},"index":0,"name":"RefreshWrapper","tagName":"hi-ul-refresh-wrapper","props":{"mergedProps":${JSON.stringify(_attrs)},},"children":[]},`)'); + }); + it('hi-refresh-wrapper-item should compile correct', () => { + const { code } = compile('', { isCustomElement: () => true }); + expect(getSsrRenderFunctionBody(code)) + .toEqual('_push(`{"id":${_ssrGetUniqueId()},"index":0,"name":"RefreshWrapperItemView","tagName":"hi-refresh-wrapper-item","props":{"mergedProps":${JSON.stringify(_attrs)},},"children":[]},`)'); + }); + it('hi-waterfall should compile correct', () => { + const { code } = compile('', { isCustomElement: () => true }); + expect(getSsrRenderFunctionBody(code)) + .toEqual('_push(`{"id":${_ssrGetUniqueId()},"index":0,"name":"WaterfallView","tagName":"hi-waterfall","props":{"mergedProps":${JSON.stringify(_attrs)},},"children":[]},`)'); + }); + it('hi-waterfall-item should compile correct', () => { + const { code } = compile('', { isCustomElement: () => true }); + expect(getSsrRenderFunctionBody(code)) + .toEqual('_push(`{"id":${_ssrGetUniqueId()},"index":0,"name":"WaterfallItem","tagName":"hi-waterfall-item","props":{"mergedProps":${JSON.stringify(_attrs)},},"children":[]},`)'); + }); + }); + describe('nested tag should compile correct', () => { + it('div with child should compile correct', () => { + const { code } = compile('
    '); + expect(getSsrRenderFunctionBody(code)) + .toEqual('_push(`{"id":${_ssrGetUniqueId()},"index":0,"name":"View","tagName":"div","props":{"mergedProps":${JSON.stringify(_attrs)},},"children":[{"id":${_ssrGetUniqueId()},"index":0,"name":"View","tagName":"div","props":{},"children":[]},]},`)'); + }); + }); + describe('attrs should compile correct', () => { + it('props and event should compile correct', () => { + const { code } = compile('
    '); + expect(getSsrRenderFunctionBody(code)).toEqual('_push(`{"id":${_ssrGetUniqueId()},"index":0,"name":"View","tagName":"div","props":{"mergedProps":${JSON.stringify(_mergeProps({id:"app",class:"wrapper"},_attrs))},"onClick":true,},"children":[]},`)'); + }); + it('binding value should compile correct', () => { + const { code } = compile('
    '); + expect(getSsrRenderFunctionBody(code)).toEqual('_push(`{"id":${_ssrGetUniqueId()},"index":0,"name":"View","tagName":"div","props":{"mergedProps":${JSON.stringify(_mergeProps({value:"test",data:_ctx.show,text:\'text\'},_attrs))},},"children":[]},`)'); + }); + it('directive v-bind should compile correct', () => { + const { code } = compile('
    '); + expect(getSsrRenderFunctionBody(code)).toEqual('_push(`{"id":${_ssrGetUniqueId()},"index":0,"name":"View","tagName":"div","props":{"mergedProps":${JSON.stringify(_mergeProps(_ctx.data,_attrs))},},"children":[]},`)'); + }); + it('static value width dynamic value should compile correct', () => { + const { code } = compile('
    '); + expect(getSsrRenderFunctionBody(code)).toEqual('_push(`{"id":${_ssrGetUniqueId()},"index":0,"name":"View","tagName":"div","props":{"mergedProps":${JSON.stringify(_mergeProps({style:[{"color":"red"},_ctx.b]},_attrs))},},"children":[]},`)'); + }); + it('custom directive should compile correct', () => { + const { code } = compile('
    '); + expect(getSsrRenderFunctionBody(code)).toEqual('const _directive_report = _resolveDirective("report") _push(`{"id":${_ssrGetUniqueId()},"index":0,"name":"View","tagName":"div","props":{"mergedProps":${JSON.stringify(_mergeProps(_attrs,_ssrGetDirectiveProps(_ctx,_directive_report,_ctx.data)))},},"children":[]},`)'); + }); + it('static key/ref should compile correct', () => { + const { code } = compile('
    '); + expect(getSsrRenderFunctionBody(code)).toEqual('_push(`{"id":${_ssrGetUniqueId()},"index":0,"name":"View","tagName":"div","props":{"mergedProps":${JSON.stringify(_mergeProps({key:"key",ref:"ref"},_attrs))},},"children":[]},`)'); + }); + it('dynamic key/ref should compile correct', () => { + const { code } = compile('
    '); + expect(getSsrRenderFunctionBody(code)).toEqual('_push(`{"id":${_ssrGetUniqueId()},"index":0,"name":"View","tagName":"div","props":{"mergedProps":${JSON.stringify(_mergeProps({key:_ctx.key,ref:_ctx.ref},_attrs))},},"children":[]},`)'); + }); + }); + describe('control flow compile', () => { + it('v-if should compile correct', () => { + const { code } = compile('
    '); + expect(getSsrRenderFunctionBody(code)).toEqual('if (_ctx.ok) {_push(`{"id":${_ssrGetUniqueId()},"index":0,"name":"View","tagName":"div","props":{"mergedProps":${JSON.stringify(_attrs)},},"children":[]},`)} else {_push(`{"id":-1,"name":"comment","props":{"text":""}},`)}'); + }); + it('v-else should compile correct', () => { + const { code } = compile('
    '); + expect(getSsrRenderFunctionBody(code)).toEqual('if (_ctx.ok) {_push(`{"id":${_ssrGetUniqueId()},"index":0,"name":"View","tagName":"div","props":{"mergedProps":${JSON.stringify(_attrs)},},"children":[]},`)} else {_push(`{"id":${_ssrGetUniqueId()},"index":0,"name":"View","tagName":"div","props":{"mergedProps":${JSON.stringify(_attrs)},},"children":[]},`)}'); + }); + it('v-else-if should compile correct', () => { + const { code } = compile('
    '); + expect(getSsrRenderFunctionBody(code)).toEqual('if (_ctx.ok) {_push(`{"id":${_ssrGetUniqueId()},"index":0,"name":"View","tagName":"div","props":{"mergedProps":${JSON.stringify(_attrs)},},"children":[]},`)} else if (_ctx.not) {_push(`{"id":${_ssrGetUniqueId()},"index":0,"name":"View","tagName":"div","props":{"mergedProps":${JSON.stringify(_attrs)},},"children":[]},`)} else {_push(`{"id":-1,"name":"comment","props":{"text":""}},`)}'); + }); + it('v-for should compile correct', () => { + const { code } = compile('
    {{ key }}
    '); + expect(getSsrRenderFunctionBody(code)).toEqual('_push(`{"id":-1,"name":"comment","props":{"text":"["}},`) _ssrRenderList(_ctx.names,(key) => {_push(`{"id":${_ssrGetUniqueId()},"index":0,"name":"View","tagName":"div","props":{},"children":[{"id":${_ssrGetUniqueId()},"index":0,"name":"Text","tagName":"span","props":{"text":"${_ssrInterpolate(key)}",},"children":[]},]},`)}) _push(`{"id":-1,"name":"comment","props":{"text":"]"}},`)'); + }); + it('v-on should compile correct', () => { + const { code } = compile('
    '); + expect(getSsrRenderFunctionBody(code)).toEqual('_push(`{"id":${_ssrGetUniqueId()},"index":0,"name":"View","tagName":"div","props":{"mergedProps":${JSON.stringify(_attrs)},},"children":[]},`)'); + }); + it('.stop should compile correct', () => { + const { code } = compile('
    '); + expect(getSsrRenderFunctionBody(code)).toEqual('_push(`{"id":${_ssrGetUniqueId()},"index":0,"name":"View","tagName":"div","props":{"mergedProps":${JSON.stringify(_attrs)},"onClick":true,},"children":[]},`)'); + }); + it('v-show should compile correct', () => { + const { code } = compile('
    '); + expect(getSsrRenderFunctionBody(code)).toEqual('_push(`{"id":${_ssrGetUniqueId()},"index":0,"name":"View","tagName":"div","props":{"mergedProps":${JSON.stringify(_mergeProps({style:(_ctx.ok) ? null :{display:"none"}},_attrs))},},"children":[]},`)'); + }); + it('input v-model should compile correct', () => { + const { code } = compile(''); + expect(getSsrRenderFunctionBody(code)).toEqual('_push(`{"id":${_ssrGetUniqueId()},"index":0,"name":"TextInput","tagName":"input","props":{"mergedProps":${JSON.stringify(_attrs)},},"children":[]},`)'); + }); + it('input v-model number should compile correct', () => { + const { code } = compile(''); + expect(getSsrRenderFunctionBody(code)).toEqual('_push(`{"id":${_ssrGetUniqueId()},"index":0,"name":"TextInput","tagName":"input","props":{"mergedProps":${JSON.stringify(_mergeProps({type:"number",value:_ctx.bar},_attrs))},},"children":[]},`)'); + }); + it('input v-model password should compile correct', () => { + const { code } = compile(''); + expect(getSsrRenderFunctionBody(code)).toEqual('_push(`{"id":${_ssrGetUniqueId()},"index":0,"name":"TextInput","tagName":"input","props":{"mergedProps":${JSON.stringify(_mergeProps({type:"password",value:_ctx.bar},_attrs))},},"children":[]},`)'); + }); + it('textarea v-model should compile correct', () => { + const { code } = compile(''); + expect(getSsrRenderFunctionBody(code)).toEqual('_push(`{"id":${_ssrGetUniqueId()},"index":0,"name":"TextInput","tagName":"textarea","props":{"mergedProps":${JSON.stringify(_attrs)},},"children":[]},`)'); + }); + }); + describe('inject css vars compile', () => { + it('base inject compile', () => { + const { code } = compile('
    ', { ssrCssVars: '{ color }' }); + expect(getSsrRenderFunctionBody(code)).toEqual('const _cssVars = {style:{color:_ctx.color}} _push(`{"id":${_ssrGetUniqueId()},"index":0,"name":"View","tagName":"div","props":{"mergedProps":${JSON.stringify(_mergeProps(_attrs,_cssVars))},},"children":[]},`)'); + }); + }); + describe('scopedId compile', () => { + it('single tag scopedId compile', () => { + const { code } = compile('
    ', { scopeId: 'data-v-12345' }); + expect(getSsrRenderFunctionBody(code)).toEqual('_push(`{"id":${_ssrGetUniqueId()},"index":0,"name":"View","tagName":"div","props":{"mergedProps":${JSON.stringify(_attrs)},"data-v-12345":"",},"children":[]},`)'); + }); + it('nested tag scopedId compile', () => { + const { code } = compile('
    ', { scopeId: 'data-v-12345' }); + expect(getSsrRenderFunctionBody(code)).toEqual('_push(`{"id":${_ssrGetUniqueId()},"index":0,"name":"View","tagName":"div","props":{"mergedProps":${JSON.stringify(_attrs)},"data-v-12345":"",},"children":[{"id":${_ssrGetUniqueId()},"index":0,"name":"View","tagName":"div","props":{"data-v-12345":"",},"children":[]},]},`)'); + }); + }); + describe('fragment compile', () => { + it('fragment tag compile', () => { + const { code } = compile('
    '); + expect(getSsrRenderFunctionBody(code)).toEqual('_push(`{"id":-1,"name":"comment","props":{"text":"["}},{"id":${_ssrGetUniqueId()},"index":0,"name":"View","tagName":"div","props":{},"children":[]},{"id":${_ssrGetUniqueId()},"index":0,"name":"View","tagName":"div","props":{},"children":[]},{"id":-1,"name":"comment","props":{"text":"]"}},`)'); + }); + }); +}); diff --git a/driver/js/packages/hippy-vue-next-compiler-ssr/__test__/native-component.test.ts b/driver/js/packages/hippy-vue-next-compiler-ssr/__test__/native-component.test.ts new file mode 100644 index 00000000000..ccea08e86e1 --- /dev/null +++ b/driver/js/packages/hippy-vue-next-compiler-ssr/__test__/native-component.test.ts @@ -0,0 +1,26 @@ +/* + * Tencent is pleased to support the open source community by making + * Hippy available. + * + * Copyright (C) 2017-2022 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * native-component unit test case todo + */ +describe('native-component.test.ts', () => { + it('native-component test', () => {}); +}); diff --git a/driver/js/packages/hippy-vue-next-compiler-ssr/__test__/setup.ts b/driver/js/packages/hippy-vue-next-compiler-ssr/__test__/setup.ts new file mode 100644 index 00000000000..4b99c01a9f7 --- /dev/null +++ b/driver/js/packages/hippy-vue-next-compiler-ssr/__test__/setup.ts @@ -0,0 +1,19 @@ +/* + * Tencent is pleased to support the open source community by making + * Hippy available. + * + * Copyright (C) 2017-2022 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ diff --git a/driver/js/packages/hippy-vue-next-compiler-ssr/__test__/slot-out.test.ts b/driver/js/packages/hippy-vue-next-compiler-ssr/__test__/slot-out.test.ts new file mode 100644 index 00000000000..44859fb9cf0 --- /dev/null +++ b/driver/js/packages/hippy-vue-next-compiler-ssr/__test__/slot-out.test.ts @@ -0,0 +1,56 @@ +/* + * Tencent is pleased to support the open source community by making + * Hippy available. + * + * Copyright (C) 2017-2022 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { compile } from '../src'; +import { getSsrRenderFunctionBody } from './utils'; + +/** + * slot-out unit test case + */ +describe('slot-out.test.ts', () => { + it('basic slot compile', () => { + const { code } = compile(''); + expect(getSsrRenderFunctionBody(code)).toEqual('_ssrRenderSlot(_ctx.$slots,"default",{},null,_push,_parent)'); + }); + it('named slot compile', () => { + const { code } = compile(''); + expect(getSsrRenderFunctionBody(code)).toEqual('_ssrRenderSlot(_ctx.$slots,"foo",{},null,_push,_parent)'); + }); + it('dynamic named slot compile', () => { + const { code } = compile(''); + expect(getSsrRenderFunctionBody(code)).toEqual('_ssrRenderSlot(_ctx.$slots,_ctx.foo,{},null,_push,_parent)'); + }); + it('props slot compile', () => { + const { code } = compile(''); + expect(getSsrRenderFunctionBody(code)).toEqual('_ssrRenderSlot(_ctx.$slots,"default",{p:1,bar:"2"},null,_push,_parent)'); + }); + it('fallback slot compile', () => { + const { code } = compile('{{ fallback }}'); + expect(getSsrRenderFunctionBody(code)).toEqual('_ssrRenderSlot(_ctx.$slots,"default",{},() => {_push(`{"id":${_ssrGetUniqueId()},"index":0,"name":"Text","tagName":"span","props":{"text":"${_ssrInterpolate(_ctx.fallback)}",},"children":[]},`)},_push,_parent)'); + }); + it('scopedId slot compile', () => { + const { code } = compile('', { scopeId: 'data-v-12345' }); + expect(getSsrRenderFunctionBody(code)).toEqual('_ssrRenderSlot(_ctx.$slots,"default",{p:1,bar:"2"},null,_push,_parent,"data-v-12345-s")'); + }); + it('scopedId with slotted:false slot compile', () => { + const { code } = compile('', { scopeId: 'data-v-12345', slotted: false }); + expect(getSsrRenderFunctionBody(code)).toEqual('_ssrRenderSlot(_ctx.$slots,"default",{p:1,bar:"2"},null,_push,_parent)'); + }); +}); diff --git a/driver/js/packages/hippy-vue-next-compiler-ssr/__test__/utils.ts b/driver/js/packages/hippy-vue-next-compiler-ssr/__test__/utils.ts new file mode 100644 index 00000000000..b5ad74717f0 --- /dev/null +++ b/driver/js/packages/hippy-vue-next-compiler-ssr/__test__/utils.ts @@ -0,0 +1,38 @@ +/* + * Tencent is pleased to support the open source community by making + * Hippy available. + * + * Copyright (C) 2017-2022 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * parse and return render function body in compiled ast + * + * @param code + */ +export function getSsrRenderFunctionBody(code: string): string { + const start = code.indexOf('function ssrRender'); + let str = code.substring(start); + str = str.substring(str.indexOf('{') + 1); + return str.replace(/\n/g, '') + .replace(/\r/g, '') + .replace(/\{\s+/g, '{') + .replace(/\s+}/g, '}') + .replace(/:\s+/g, ':') + .replace(/,\s+/g, ',') + .replace(/}$/, '') + .trim(); +} diff --git a/driver/js/packages/hippy-vue-next-compiler-ssr/__test__/vue-built-in.test.ts b/driver/js/packages/hippy-vue-next-compiler-ssr/__test__/vue-built-in.test.ts new file mode 100644 index 00000000000..393b97d39ba --- /dev/null +++ b/driver/js/packages/hippy-vue-next-compiler-ssr/__test__/vue-built-in.test.ts @@ -0,0 +1,58 @@ +/* + * Tencent is pleased to support the open source community by making + * Hippy available. + * + * Copyright (C) 2017-2022 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { compile } from '../src'; +import { getSsrRenderFunctionBody } from './utils'; + +/** + * hippy-vue-next does not support these vue built-in tag now, just for test unit case + */ +describe('vue-built-in.test.ts', () => { + describe('transition-group compile', () => { + it('tag compile', () => { + const { code } = compile('
    '); + expect(getSsrRenderFunctionBody(code)).toEqual('_push(`{"id":-1,"name":"comment","props":{"text":"["}},`) _ssrRenderList(_ctx.list,(i) => {_push(`{"id":${_ssrGetUniqueId()},"index":0,"name":"View","tagName":"div","props":{},"children":[]},`)}) _push(`{"id":-1,"name":"comment","props":{"text":"]"}},`)'); + }); + it('static tag compile', () => { + const { code } = compile('
    '); + expect(getSsrRenderFunctionBody(code)).toEqual('_push(``) _ssrRenderList(_ctx.list,(i) => {_push(`{"id":${_ssrGetUniqueId()},"index":0,"name":"View","tagName":"div","props":{},"children":[]},`)}) _push(``)'); + }); + it('dynamic tag compile', () => { + const { code } = compile('
    '); + expect(getSsrRenderFunctionBody(code)).toEqual('_push(`<${_ctx.foo}${_ssrRenderAttrs(_attrs)}>`) _ssrRenderList(_ctx.list,(i) => {_push(`{"id":${_ssrGetUniqueId()},"index":0,"name":"View","tagName":"div","props":{},"children":[]},`)}) _push(``)'); + }); + it('props compile', () => { + const { code } = compile(''); + expect(getSsrRenderFunctionBody(code)).toEqual('_push(``)'); + }); + }); + describe('suspense compile', () => { + it('component compile', () => { + const { code } = compile(''); + expect(getSsrRenderFunctionBody(code)).toEqual('const _component_foo = _resolveComponent("foo") _ssrRenderSuspense(_push,{default:() => {_push(_ssrRenderComponent(_component_foo,null,null,_parent))},_:1 /* STABLE */})'); + }); + }); + describe('teleport compile', () => { + it('component compile', () => { + const { code } = compile(''); + expect(getSsrRenderFunctionBody(code)).toEqual('const _component_foo = _resolveComponent("foo") _ssrRenderTeleport(_push,(_push) => {_push(_ssrRenderComponent(_component_foo,null,null,_parent))},_ctx.target,false,_parent)'); + }); + }); +}); diff --git a/driver/js/packages/hippy-vue-next-compiler-ssr/api-extractor.json b/driver/js/packages/hippy-vue-next-compiler-ssr/api-extractor.json new file mode 100644 index 00000000000..946f9459d3a --- /dev/null +++ b/driver/js/packages/hippy-vue-next-compiler-ssr/api-extractor.json @@ -0,0 +1,38 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", + "mainEntryPointFilePath": "./dist/hippy-vue-next-compiler-ssr/src/index.d.ts", + "apiReport": { + "enabled": false + }, + "docModel": { + "enabled": false + }, + "dtsRollup": { + "enabled": true, + "untrimmedFilePath": "./dist/index.d.ts" + }, + "tsdocMetadata": { + "enabled": false + }, + "messages": { + "compilerMessageReporting": { + "TS2374": { + "logLevel": "none" + }, + "TS6200": { + "logLevel": "none" + }, + "TS2403": { + "logLevel": "none" + } + }, + "extractorMessageReporting": { + "ae-forgotten-export": { + "logLevel": "none" + }, + "ae-wrong-input-file-type": { + "logLevel": "none" + } + } + } +} diff --git a/driver/js/packages/hippy-vue-next-compiler-ssr/package-lock.json b/driver/js/packages/hippy-vue-next-compiler-ssr/package-lock.json new file mode 100644 index 00000000000..577a33dcc77 --- /dev/null +++ b/driver/js/packages/hippy-vue-next-compiler-ssr/package-lock.json @@ -0,0 +1,115 @@ +{ + "name": "@hippy/vue-next-compiler-ssr", + "version": "unspecified", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "@hippy/vue-next-compiler-ssr", + "version": "unspecified", + "license": "Apache-2.0", + "dependencies": { + "@vue/compiler-core": "^3.2.21", + "@vue/compiler-dom": "^3.2.21", + "@vue/shared": "^3.2.21" + } + }, + "node_modules/@babel/parser": { + "version": "7.21.2", + "resolved": "https://mirrors.tencent.com/npm/@babel%2fparser/-/parser-7.21.2.tgz", + "integrity": "sha512-URpaIJQwEkEC2T9Kn+Ai6Xe/02iNaVCuT/PtoRz3GPVJVDpPd7mLo+VddTbhCRU9TXqW5mSrQfXZyi8kDKOVpQ==", + "license": "MIT", + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.2.47", + "resolved": "https://mirrors.tencent.com/npm/@vue%2fcompiler-core/-/compiler-core-3.2.47.tgz", + "integrity": "sha512-p4D7FDnQb7+YJmO2iPEv0SQNeNzcbHdGByJDsT4lynf63AFkOTFN07HsiRSvjGo0QrxR/o3d0hUyNCUnBU2Tig==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.16.4", + "@vue/shared": "3.2.47", + "estree-walker": "^2.0.2", + "source-map": "^0.6.1" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.2.47", + "resolved": "https://mirrors.tencent.com/npm/@vue%2fcompiler-dom/-/compiler-dom-3.2.47.tgz", + "integrity": "sha512-dBBnEHEPoftUiS03a4ggEig74J2YBZ2UIeyfpcRM2tavgMWo4bsEfgCGsu+uJIL/vax9S+JztH8NmQerUo7shQ==", + "license": "MIT", + "dependencies": { + "@vue/compiler-core": "3.2.47", + "@vue/shared": "3.2.47" + } + }, + "node_modules/@vue/shared": { + "version": "3.2.47", + "resolved": "https://mirrors.tencent.com/npm/@vue%2fshared/-/shared-3.2.47.tgz", + "integrity": "sha512-BHGyyGN3Q97EZx0taMQ+OLNuZcW3d37ZEVmEAyeoA9ERdGvm9Irc/0Fua8SNyOtV1w6BS4q25wbMzJujO9HIfQ==", + "license": "MIT" + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://mirrors.tencent.com/npm/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "license": "MIT" + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://mirrors.tencent.com/npm/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + } + }, + "dependencies": { + "@babel/parser": { + "version": "7.21.2", + "resolved": "https://mirrors.tencent.com/npm/@babel%2fparser/-/parser-7.21.2.tgz", + "integrity": "sha512-URpaIJQwEkEC2T9Kn+Ai6Xe/02iNaVCuT/PtoRz3GPVJVDpPd7mLo+VddTbhCRU9TXqW5mSrQfXZyi8kDKOVpQ==" + }, + "@vue/compiler-core": { + "version": "3.2.47", + "resolved": "https://mirrors.tencent.com/npm/@vue%2fcompiler-core/-/compiler-core-3.2.47.tgz", + "integrity": "sha512-p4D7FDnQb7+YJmO2iPEv0SQNeNzcbHdGByJDsT4lynf63AFkOTFN07HsiRSvjGo0QrxR/o3d0hUyNCUnBU2Tig==", + "requires": { + "@babel/parser": "^7.16.4", + "@vue/shared": "3.2.47", + "estree-walker": "^2.0.2", + "source-map": "^0.6.1" + } + }, + "@vue/compiler-dom": { + "version": "3.2.47", + "resolved": "https://mirrors.tencent.com/npm/@vue%2fcompiler-dom/-/compiler-dom-3.2.47.tgz", + "integrity": "sha512-dBBnEHEPoftUiS03a4ggEig74J2YBZ2UIeyfpcRM2tavgMWo4bsEfgCGsu+uJIL/vax9S+JztH8NmQerUo7shQ==", + "requires": { + "@vue/compiler-core": "3.2.47", + "@vue/shared": "3.2.47" + } + }, + "@vue/shared": { + "version": "3.2.47", + "resolved": "https://mirrors.tencent.com/npm/@vue%2fshared/-/shared-3.2.47.tgz", + "integrity": "sha512-BHGyyGN3Q97EZx0taMQ+OLNuZcW3d37ZEVmEAyeoA9ERdGvm9Irc/0Fua8SNyOtV1w6BS4q25wbMzJujO9HIfQ==" + }, + "estree-walker": { + "version": "2.0.2", + "resolved": "https://mirrors.tencent.com/npm/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://mirrors.tencent.com/npm/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } +} diff --git a/driver/js/packages/hippy-vue-next-compiler-ssr/package.json b/driver/js/packages/hippy-vue-next-compiler-ssr/package.json new file mode 100644 index 00000000000..1d90a5d4e5d --- /dev/null +++ b/driver/js/packages/hippy-vue-next-compiler-ssr/package.json @@ -0,0 +1,49 @@ +{ + "name": "@hippy/vue-next-compiler-ssr", + "version": "unspecified", + "description": "Vue-Next server render compiler for Hippy native framework", + "author": "OpenHippy Team", + "homepage": "https://hippyjs.org", + "license": "Apache-2.0", + "entry": "./src/index.ts", + "repository": "https://github.com/Tencent/Hippy", + "bugs": { + "url": "https://github.com/Tencent/Hippy/issues" + }, + "keywords": [ + "hippy", + "hippy-vue-next", + "server-renderer", + "compiler" + ], + "main": "./dist/index.js", + "types": "dist/index.d.ts", + "files": [ + "dist/index.js", + "dist/index.d.ts", + "README.md" + ], + "dependencies": { + "@vue/compiler-core": "^3.2.46", + "@vue/compiler-dom": "^3.2.46", + "@vue/shared": "^3.2.46" + }, + "jest": { + "moduleNameMapper": { + "^src/(.*)$": "/src/$1", + "@hippy-vue-next-server-renderer/(.*)$": "/../hippy-vue-next-server-renderer/src/$1" + }, + "modulePaths": [ + "" + ], + "preset": "ts-jest", + "setupFiles": [ + "/__test__/setup.ts" + ], + "testEnvironment": "node", + "testMatch": [ + "**/*.test.ts" + ], + "verbose": true + } +} diff --git a/driver/js/packages/hippy-vue-next-compiler-ssr/src/errors.ts b/driver/js/packages/hippy-vue-next-compiler-ssr/src/errors.ts new file mode 100644 index 00000000000..4ccc84f17fd --- /dev/null +++ b/driver/js/packages/hippy-vue-next-compiler-ssr/src/errors.ts @@ -0,0 +1,54 @@ +/* + * Tencent is pleased to support the open source community by making + * Hippy available. + * + * Copyright (C) 2022 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* eslint-disable @typescript-eslint/no-use-before-define */ +import { + type SourceLocation, + type CompilerError, + createCompilerError, + DOMErrorCodes, +} from '@vue/compiler-dom'; + +// eslint-disable-next-line @typescript-eslint/naming-convention +export interface SSRCompilerError extends CompilerError { + code: SSRErrorCodes; +} + +export function createSSRCompilerError( + code: SSRErrorCodes, + loc?: SourceLocation, +): SSRCompilerError { + return createCompilerError(code, loc, SSRErrorMessages) as SSRCompilerError; +} + +export const enum SSRErrorCodes { + // eslint-disable-next-line @typescript-eslint/prefer-literal-enum-member + X_SSR_UNSAFE_ATTR_NAME = DOMErrorCodes.__EXTEND_POINT__, + X_SSR_NO_TELEPORT_TARGET, + X_SSR_INVALID_AST_NODE, +} + +export const SSRErrorMessages: { [code: number]: string } = { + [SSRErrorCodes.X_SSR_UNSAFE_ATTR_NAME]: 'Unsafe attribute name for SSR.', + [SSRErrorCodes.X_SSR_NO_TELEPORT_TARGET]: + 'Missing the \'to\' prop on teleport element.', + [SSRErrorCodes.X_SSR_INVALID_AST_NODE]: + 'Invalid AST node during SSR transform.', +}; diff --git a/driver/js/packages/hippy-vue-next-compiler-ssr/src/index.ts b/driver/js/packages/hippy-vue-next-compiler-ssr/src/index.ts new file mode 100644 index 00000000000..a0d0eb26bd4 --- /dev/null +++ b/driver/js/packages/hippy-vue-next-compiler-ssr/src/index.ts @@ -0,0 +1,122 @@ +/* + * Tencent is pleased to support the open source community by making + * Hippy available. + * + * Copyright (C) 2022 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* eslint-disable no-param-reassign */ +import { + type CodegenResult, + baseParse, + parserOptions, + transform, + generate, + type CompilerOptions, + transformExpression, + trackVForSlotScopes, + trackSlotScopes, + noopDirectiveTransform, + transformBind, + transformStyle, + transformOn, +} from '@vue/compiler-dom'; + +import { ssrCodegenTransform } from './ssrCodegenTransform'; +import { ssrInjectCssVars } from './transforms/ssrInjectCssVars'; +import { ssrInjectFallthroughAttrs } from './transforms/ssrInjectFallthroughAttrs'; +import { + ssrTransformComponent, + rawOptionsMap, +} from './transforms/ssrTransformComponent'; +import { ssrTransformElement } from './transforms/ssrTransformElement'; +import { ssrTransformSlotOutlet } from './transforms/ssrTransformSlotOutlet'; +import { ssrTransformFor } from './transforms/ssrVFor'; +import { ssrTransformIf } from './transforms/ssrVIf'; +import { ssrTransformModel } from './transforms/ssrVModel'; +import { ssrTransformShow } from './transforms/ssrVShow'; + +/** + * ssr compile + * + * @param template - template string + * @param options - compile options + * + * @public + */ +export function compile( + template: string, + options: CompilerOptions = {}, +): CodegenResult { + options = { + ...options, + // apply DOM-specific parsing options + ...parserOptions, + ssr: true, + inSSR: true, + scopeId: options.mode === 'function' ? null : options.scopeId, + // always prefix since compiler-ssr doesn't have size concern + prefixIdentifiers: true, + // disable optimizations that are unnecessary for ssr + cacheHandlers: false, + hoistStatic: false, + }; + + const ast = baseParse(template, options); + + // Save raw options for AST. This is needed when performing sub-transforms + // on slot vnode branches. + rawOptionsMap.set(ast, options); + + transform(ast, { + ...options, + hoistStatic: false, + nodeTransforms: [ + ssrTransformIf, + ssrTransformFor, + trackVForSlotScopes, + transformExpression, + ssrTransformSlotOutlet, + ssrInjectFallthroughAttrs, + ssrInjectCssVars, + ssrTransformElement, + ssrTransformComponent, + trackSlotScopes, + transformStyle, + ...(options.nodeTransforms ?? []), // user custom transforms + ], + directiveTransforms: { + // reusing core v-bind + bind: transformBind, + on: transformOn, + // model and show has dedicated SSR handling + model: ssrTransformModel, + show: ssrTransformShow, + // the following are ignored during SSR + // on: noopDirectiveTransform, + cloak: noopDirectiveTransform, + once: noopDirectiveTransform, + memo: noopDirectiveTransform, + ...(options.directiveTransforms ?? {}), // user custom transforms + }, + }); + + // traverse the template AST and convert into SSR codegen AST + // by replacing ast.codegenNode. + ssrCodegenTransform(ast, options); + + return generate(ast, options); +} diff --git a/driver/js/packages/hippy-vue-next-compiler-ssr/src/runtimeHelpers.ts b/driver/js/packages/hippy-vue-next-compiler-ssr/src/runtimeHelpers.ts new file mode 100644 index 00000000000..17eada4737b --- /dev/null +++ b/driver/js/packages/hippy-vue-next-compiler-ssr/src/runtimeHelpers.ts @@ -0,0 +1,70 @@ +/* + * Tencent is pleased to support the open source community by making + * Hippy available. + * + * Copyright (C) 2022 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { registerRuntimeHelpers } from '@vue/compiler-dom'; + +export const SSR_INTERPOLATE = Symbol('ssrInterpolate'); +export const SSR_RENDER_VNODE = Symbol('ssrRenderVNode'); +export const SSR_RENDER_COMPONENT = Symbol('ssrRenderComponent'); +export const SSR_RENDER_SLOT = Symbol('ssrRenderSlot'); +export const SSR_RENDER_SLOT_INNER = Symbol('ssrRenderSlotInner'); +export const SSR_RENDER_CLASS = Symbol('ssrRenderClass'); +export const SSR_RENDER_STYLE = Symbol('ssrRenderStyle'); +export const SSR_RENDER_ATTRS = Symbol('ssrRenderAttrs'); +export const SSR_RENDER_ATTR = Symbol('ssrRenderAttr'); +export const SSR_RENDER_DYNAMIC_ATTR = Symbol('ssrRenderDynamicAttr'); +export const SSR_RENDER_LIST = Symbol('ssrRenderList'); +export const SSR_INCLUDE_BOOLEAN_ATTR = Symbol('ssrIncludeBooleanAttr'); +export const SSR_LOOSE_EQUAL = Symbol('ssrLooseEqual'); +export const SSR_LOOSE_CONTAIN = Symbol('ssrLooseContain'); +export const SSR_RENDER_DYNAMIC_MODEL = Symbol('ssrRenderDynamicModel'); +export const SSR_GET_DYNAMIC_MODEL_PROPS = Symbol('ssrGetDynamicModelProps'); +export const SSR_RENDER_TELEPORT = Symbol('ssrRenderTeleport'); +export const SSR_RENDER_SUSPENSE = Symbol('ssrRenderSuspense'); +export const SSR_GET_DIRECTIVE_PROPS = Symbol('ssrGetDirectiveProps'); +// provided by @hippy/vue-next-server-renderer +export const SSR_GET_UNIQUEID = Symbol('ssrGetUniqueId'); + +export const ssrHelpers = { + [SSR_INTERPOLATE]: 'ssrInterpolate', + [SSR_RENDER_VNODE]: 'ssrRenderVNode', + [SSR_RENDER_COMPONENT]: 'ssrRenderComponent', + [SSR_RENDER_SLOT]: 'ssrRenderSlot', + [SSR_RENDER_SLOT_INNER]: 'ssrRenderSlotInner', + [SSR_RENDER_CLASS]: 'ssrRenderClass', + [SSR_RENDER_STYLE]: 'ssrRenderStyle', + [SSR_RENDER_ATTRS]: 'ssrRenderAttrs', + [SSR_RENDER_ATTR]: 'ssrRenderAttr', + [SSR_RENDER_DYNAMIC_ATTR]: 'ssrRenderDynamicAttr', + [SSR_RENDER_LIST]: 'ssrRenderList', + [SSR_INCLUDE_BOOLEAN_ATTR]: 'ssrIncludeBooleanAttr', + [SSR_LOOSE_EQUAL]: 'ssrLooseEqual', + [SSR_LOOSE_CONTAIN]: 'ssrLooseContain', + [SSR_RENDER_DYNAMIC_MODEL]: 'ssrRenderDynamicModel', + [SSR_GET_DYNAMIC_MODEL_PROPS]: 'ssrGetDynamicModelProps', + [SSR_RENDER_TELEPORT]: 'ssrRenderTeleport', + [SSR_RENDER_SUSPENSE]: 'ssrRenderSuspense', + [SSR_GET_DIRECTIVE_PROPS]: 'ssrGetDirectiveProps', + [SSR_GET_UNIQUEID]: 'ssrGetUniqueId', +}; + +// Note: these are helpers imported from @vue/server-renderer +// make sure the names match! +registerRuntimeHelpers(ssrHelpers); diff --git a/driver/js/packages/hippy-vue-next-compiler-ssr/src/ssrCodegenTransform.ts b/driver/js/packages/hippy-vue-next-compiler-ssr/src/ssrCodegenTransform.ts new file mode 100644 index 00000000000..3aba6ca273f --- /dev/null +++ b/driver/js/packages/hippy-vue-next-compiler-ssr/src/ssrCodegenTransform.ts @@ -0,0 +1,262 @@ +/* + * Tencent is pleased to support the open source community by making + * Hippy available. + * + * Copyright (C) 2022 THL A29 Limited, a Tencent company. + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable no-param-reassign */ +/* eslint-disable import/no-cycle */ +/* eslint-disable @typescript-eslint/no-use-before-define */ +import { + type RootNode, + type BlockStatement, + type TemplateLiteral, + createCallExpression, + createTemplateLiteral, + NodeTypes, + type TemplateChildNode, + ElementTypes, + createBlockStatement, + type CompilerOptions, + type IfStatement, + type CallExpression, + isText, + processExpression, + createSimpleExpression, + createCompoundExpression, + createTransformContext, + createRoot, +} from '@vue/compiler-dom'; +import { isString } from '@vue/shared'; + +import { createSSRCompilerError, SSRErrorCodes } from './errors'; +import { ssrHelpers } from './runtimeHelpers'; +import { ssrProcessComponent } from './transforms/ssrTransformComponent'; +import { ssrProcessElement } from './transforms/ssrTransformElement'; +import { ssrProcessSlotOutlet } from './transforms/ssrTransformSlotOutlet'; +import { ssrProcessFor } from './transforms/ssrVFor'; +import { ssrProcessIf } from './transforms/ssrVIf'; + +// Because SSR codegen output is completely different from client-side output +// (e.g. multiple elements can be concatenated into a single template literal +// instead of each getting a corresponding call), we need to apply an extra +// transform pass to convert the template AST into a fresh JS AST before +// passing it to codegen. + +export function ssrCodegenTransform( + ast: RootNode, + options: CompilerOptions, +): void { + const context = createSSRTransformContext(ast, options); + + // inject SFC diff --git a/driver/js/examples/hippy-vue-next-ssr-demo/src/assets/defaultSource.jpg b/driver/js/examples/hippy-vue-next-ssr-demo/src/assets/defaultSource.jpg new file mode 100644 index 0000000000000000000000000000000000000000..833417ea2fb9732e462397b44094599e2b96a56d GIT binary patch literal 42281 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 ZIcDMS&LgEgX}y2tW=580o*THu{XgQR4!HmT literal 0 HcmV?d00001 diff --git a/driver/js/examples/hippy-vue-next-ssr-demo/src/assets/logo.png b/driver/js/examples/hippy-vue-next-ssr-demo/src/assets/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..f3d2503fc2a44b5053b0837ebea6e87a2d339a43 GIT binary patch literal 6849 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 literal 0 HcmV?d00001 diff --git a/driver/js/examples/hippy-vue-next-ssr-demo/src/components/demo/demo-button.vue b/driver/js/examples/hippy-vue-next-ssr-demo/src/components/demo/demo-button.vue new file mode 100644 index 00000000000..00c06a74eb8 --- /dev/null +++ b/driver/js/examples/hippy-vue-next-ssr-demo/src/components/demo/demo-button.vue @@ -0,0 +1,130 @@ + + + + + diff --git a/driver/js/examples/hippy-vue-next-ssr-demo/src/components/demo/demo-div.vue b/driver/js/examples/hippy-vue-next-ssr-demo/src/components/demo/demo-div.vue new file mode 100644 index 00000000000..fe2cf2d6179 --- /dev/null +++ b/driver/js/examples/hippy-vue-next-ssr-demo/src/components/demo/demo-div.vue @@ -0,0 +1,343 @@ + + + + + diff --git a/driver/js/examples/hippy-vue-next-ssr-demo/src/components/demo/demo-dynamicimport.vue b/driver/js/examples/hippy-vue-next-ssr-demo/src/components/demo/demo-dynamicimport.vue new file mode 100644 index 00000000000..1b231d3702e --- /dev/null +++ b/driver/js/examples/hippy-vue-next-ssr-demo/src/components/demo/demo-dynamicimport.vue @@ -0,0 +1,101 @@ + + + + + diff --git a/driver/js/examples/hippy-vue-next-ssr-demo/src/components/demo/demo-iframe.vue b/driver/js/examples/hippy-vue-next-ssr-demo/src/components/demo/demo-iframe.vue new file mode 100644 index 00000000000..aec04f96376 --- /dev/null +++ b/driver/js/examples/hippy-vue-next-ssr-demo/src/components/demo/demo-iframe.vue @@ -0,0 +1,125 @@ +