├── packages ├── cli │ ├── .npmignore │ ├── .gitignore │ ├── src │ │ ├── constants │ │ │ └── index.ts │ │ ├── commands │ │ │ ├── doc.ts │ │ │ ├── test.ts │ │ │ ├── build.ts │ │ │ ├── start.ts │ │ │ ├── lint.ts │ │ │ └── create.ts │ │ ├── helpers │ │ │ ├── createBuildConfig.ts │ │ │ ├── utils.ts │ │ │ ├── logger.ts │ │ │ └── createRollupConfig.ts │ │ ├── types │ │ │ └── index.ts │ │ └── cli.ts │ ├── tsconfig.json │ ├── bin │ │ └── svere-cli │ ├── CHANGELOG.md │ ├── __tests__ │ │ └── cli.test.ts │ ├── package.json │ ├── README_ZH.md │ └── README.md └── core │ ├── .npmignore │ ├── src │ ├── vue │ │ ├── .gitignore │ │ ├── jest.config.js │ │ ├── __tests__ │ │ │ ├── __snapshots__ │ │ │ │ └── Vue.test.tsx.snap │ │ │ └── Vue.test.tsx │ │ ├── tsconfig.json │ │ ├── rollup.config.js │ │ ├── package.json │ │ └── index.ts │ ├── vue3 │ │ ├── .gitignore │ │ ├── jest.config.js │ │ ├── __tests__ │ │ │ ├── __snapshots__ │ │ │ │ └── Vue.test.tsx.snap │ │ │ └── Vue.test.tsx │ │ ├── tsconfig.json │ │ ├── rollup.config.js │ │ ├── package.json │ │ └── index.ts │ ├── react │ │ ├── .gitignore │ │ ├── jest.config.js │ │ ├── tsconfig.json │ │ ├── __tests__ │ │ │ ├── __snapshots__ │ │ │ │ └── React.test.tsx.snap │ │ │ └── React.test.tsx │ │ ├── rollup.config.js │ │ ├── package.json │ │ └── index.ts │ ├── helpers │ │ └── index.ts │ └── types │ │ └── index.d.ts │ ├── yarn.lock │ ├── .gitignore │ ├── package.json │ ├── CHANGELOG.md │ ├── tsconfig.json │ ├── dist │ ├── vue.mjs │ ├── react.mjs │ ├── vue3.mjs │ ├── vue.js │ ├── vue3.js │ └── react.js │ ├── README_ZH.md │ └── README.md ├── examples ├── .gitignore ├── src │ ├── index.ts │ ├── Component.svelte │ └── __tests__ │ │ └── Component.test.ts ├── svelte.config.js ├── tsconfig.json ├── rollup.config.js ├── package.json └── dist │ ├── index.mjs │ └── index.js ├── .gitignore ├── package.json ├── lerna.json ├── LICENSE └── README.md /packages/cli/.npmignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /packages/core/.npmignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /examples/.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | .DS_Store 3 | -------------------------------------------------------------------------------- /examples/src/index.ts: -------------------------------------------------------------------------------- 1 | export { default as default } from './Component.svelte'; -------------------------------------------------------------------------------- /packages/core/src/vue/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | npm-debug.log 4 | coverage 5 | .nyc_output 6 | .vscode 7 | -------------------------------------------------------------------------------- /packages/core/src/vue3/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | npm-debug.log 4 | coverage 5 | .nyc_output 6 | .vscode 7 | -------------------------------------------------------------------------------- /packages/core/src/react/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | npm-debug.log 4 | coverage 5 | .nyc_output 6 | .vscode 7 | -------------------------------------------------------------------------------- /packages/core/yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | -------------------------------------------------------------------------------- /packages/cli/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | npm-debug.log 4 | coverage 5 | .nyc_output 6 | dist 7 | build 8 | .vscode 9 | -------------------------------------------------------------------------------- /examples/svelte.config.js: -------------------------------------------------------------------------------- 1 | const sveltePreprocess = require("svelte-preprocess"); 2 | 3 | module.exports = { 4 | preprocess: sveltePreprocess(), 5 | }; 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | node_modules 3 | dist 4 | *.log 5 | .vscode 6 | .idea 7 | coverage 8 | .DS_Store 9 | *.tsbuildinfo 10 | -------------------------------------------------------------------------------- /packages/core/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /src/react/node_modules/ 4 | /src/vue/node_modules/ 5 | /src/vue3/node_modules/ 6 | npm-debug.log 7 | coverage 8 | .nyc_output 9 | .vscode 10 | -------------------------------------------------------------------------------- /packages/core/src/helpers/index.ts: -------------------------------------------------------------------------------- 1 | const resolveWrapperProps = (wrapperProps?: WrapperProps) => ({ 2 | element: "div", 3 | id: "svelte-wrapper", 4 | ...wrapperProps 5 | }); 6 | export default resolveWrapperProps -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "root", 3 | "private": true, 4 | "devDependencies": { 5 | "lerna": "^4.0.0" 6 | }, 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/FE-PIRL/svere" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "packages": [ 3 | "packages/*" 4 | ], 5 | "version": "independent", 6 | "command": { 7 | "publish": { 8 | "conventionalCommits": true, 9 | "access": "public", 10 | "allowBranch": ["master", "feature/*"] 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /examples/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tsconfig/svelte/tsconfig.json", 3 | 4 | "include": ["src/**/*"], 5 | "exclude": ["node_modules/*", "dist/*"], 6 | "compilerOptions": { 7 | "types": ["node", "jest", "@testing-library/jest-dom"], 8 | "typeRoots": ["./node_modules/@types"] 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/core/src/vue/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: 'ts-jest', 3 | testEnvironment: 'jsdom', 4 | transform: { 5 | "^.+\\.tsx?$": "ts-jest" 6 | }, 7 | testRegex: "(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$", 8 | moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"], 9 | collectCoverage: false, 10 | 11 | }; -------------------------------------------------------------------------------- /packages/core/src/vue3/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: 'ts-jest', 3 | testEnvironment: 'jsdom', 4 | transform: { 5 | "^.+\\.tsx?$": "ts-jest" 6 | }, 7 | testRegex: "(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$", 8 | moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"], 9 | collectCoverage: false, 10 | 11 | }; -------------------------------------------------------------------------------- /packages/core/src/react/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: 'ts-jest', 3 | testEnvironment: 'jsdom', 4 | transform: { 5 | "^.+\\.tsx?$": "ts-jest" 6 | }, 7 | testRegex: "(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$", 8 | moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"], 9 | collectCoverage: false, 10 | 11 | }; -------------------------------------------------------------------------------- /packages/core/src/types/index.d.ts: -------------------------------------------------------------------------------- 1 | interface ComponentOptions { 2 | target: HTMLElement | null; 3 | anchor: HTMLElement | null; 4 | } 5 | 6 | type SvelteComponent = import('svelte').SvelteComponent; 7 | 8 | type SvelteComponentConstructor = new(ComponentOptions) => SvelteComponent; 9 | 10 | type WrapperProps = { 11 | element?: string | FunctionComponent<{}> | ComponentClass<{}, any>; 12 | id?: string; 13 | className?: string; 14 | styles?: {}; 15 | }; 16 | -------------------------------------------------------------------------------- /packages/core/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@svere/core", 3 | "version": "1.0.6", 4 | "description": "svelte adapter for react/vue/vue3", 5 | "files": [ 6 | "dist" 7 | ], 8 | "keywords": [ 9 | "svelte", 10 | "adapter", 11 | "react", 12 | "vue", 13 | "vue3", 14 | "cfc" 15 | ], 16 | "author": "benyasin", 17 | "license": "MIT", 18 | "gitHead": "669a79e88eced69e59361d3e9e6acac01d6f10ea", 19 | "homepage": "https://github.com/FE-PIRL/svere#readme", 20 | "bugs": { 21 | "url": "https://github.com/FE-PIRL/svere/issues" 22 | }, 23 | "repository": { 24 | "type": "git", 25 | "url": "git+https://github.com:FE-PIRL/svere.git" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /packages/cli/src/constants/index.ts: -------------------------------------------------------------------------------- 1 | import * as fs from "fs-extra"; 2 | import path from "path"; 3 | export const appName = "svere"; 4 | export const appDirectory = fs.realpathSync( 5 | path.resolve(__dirname + "../../../") 6 | ); 7 | export const resolvePath = function(relativePath: string) { 8 | return path.resolve(appDirectory, relativePath); 9 | }; 10 | export const paths = { 11 | appPackageJson: resolvePath("package.json"), 12 | tsconfigJson: resolvePath("tsconfig.json"), 13 | appRoot: resolvePath("."), 14 | appSrc: resolvePath("src"), 15 | appDist: resolvePath("dist"), 16 | appPublic: resolvePath("public"), 17 | appConfig: resolvePath("svere.config.js"), 18 | jestConfig: resolvePath("jest.config.js") 19 | }; 20 | -------------------------------------------------------------------------------- /packages/core/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | ## [1.0.6](https://github.com/FE-PIRL/svere/compare/@svere/core@1.0.4...@svere/core@1.0.6) (2022-01-25) 7 | 8 | **Note:** Version bump only for package @svere/core 9 | 10 | 11 | 12 | 13 | 14 | ## [1.0.4](https://github.com/FE-PIRL/svere/compare/@svere/core@1.0.1...@svere/core@1.0.4) (2022-01-25) 15 | 16 | 17 | 18 | ## 1.0.2 (2021-05-21) 19 | 20 | **Note:** Version bump only for package @svere/core 21 | 22 | 23 | 24 | 25 | 26 | ## 1.0.1 (2021-05-19) 27 | 28 | **Note:** Version bump only for package @svere/core 29 | -------------------------------------------------------------------------------- /packages/core/src/vue/__tests__/__snapshots__/Vue.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Vue custom wrapper 1`] = ` 4 | "
5 |
6 |

Hello ben!

7 |
8 |
" 9 | `; 10 | 11 | exports[`Vue should render 1`] = ` 12 | "
13 |
14 |

Hello ben!

15 |
16 |
" 17 | `; 18 | -------------------------------------------------------------------------------- /packages/core/src/vue3/__tests__/__snapshots__/Vue.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Vue custom wrapper 1`] = ` 4 | "
5 |
6 |

Hello ben!

7 |
8 |
" 9 | `; 10 | 11 | exports[`Vue should render 1`] = ` 12 | "
13 |
14 |

Hello ben!

15 |
16 |
" 17 | `; 18 | -------------------------------------------------------------------------------- /packages/cli/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "jsx": "preserve", 5 | "resolveJsonModule": true, 6 | "sourceMap": true, 7 | "outDir": "build", 8 | "strict": false, 9 | "target": "es5", 10 | "declaration": true, 11 | "declarationDir": "build/types", 12 | "esModuleInterop": true, 13 | "allowSyntheticDefaultImports": true, 14 | "skipLibCheck": true, 15 | "skipDefaultLibCheck": true, 16 | "baseUrl": "./", 17 | "rootDir": "./src", 18 | "paths": { 19 | "@svere/core": ["../core/src"] 20 | } 21 | }, 22 | "references": [{ "path": "../core" }], 23 | "ts-node": { 24 | "typeCheck": false 25 | }, 26 | "include": ["src/**/*"], 27 | "exclude": ["node_modules"] 28 | } 29 | -------------------------------------------------------------------------------- /examples/rollup.config.js: -------------------------------------------------------------------------------- 1 | import svelte from 'rollup-plugin-svelte'; 2 | import resolve from '@rollup/plugin-node-resolve'; 3 | import pkg from './package.json'; 4 | import autoPreprocess from 'svelte-preprocess'; 5 | import typescript from '@rollup/plugin-typescript'; 6 | 7 | const name = pkg.name 8 | .replace(/^(@\S+\/)?(svelte-)?(\S+)/, '$3') 9 | .replace(/^\w/, m => m.toUpperCase()) 10 | .replace(/-\w/g, m => m[1].toUpperCase()); 11 | const production = !process.env.ROLLUP_WATCH; 12 | 13 | export default { 14 | input: 'src/index.ts', 15 | output: [ 16 | { file: pkg.module, 'format': 'es' }, 17 | { file: pkg.main, 'format': 'umd', name } 18 | ], 19 | plugins: [ 20 | svelte({ 21 | preprocess: autoPreprocess() 22 | }), 23 | typescript({ sourceMap: !production }), 24 | resolve() 25 | ], 26 | 27 | }; 28 | -------------------------------------------------------------------------------- /packages/cli/bin/svere-cli: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /* tslint:disable */ 4 | // check if we're running in dev mode 5 | var devMode = require('fs').existsSync(`${__dirname}/../src`) 6 | // or want to "force" running the compiled version with --compiled-build 7 | var wantsCompiled = process.argv.indexOf('--compiled-build') >= 0 8 | 9 | var argv = process.argv 10 | 11 | if (wantsCompiled || !devMode) { 12 | // this runs from the compiled javascript source 13 | require(`${__dirname}/../build/cli`).main(argv) 14 | } else { 15 | // this runs from the typescript source (for dev only) 16 | // hook into ts-node so we can run typescript on the fly 17 | require('ts-node').register({ project: `${__dirname}/../tsconfig.json` }) 18 | // run the CLI with the current process arguments 19 | require(`${__dirname}/../src/cli`).main(argv) 20 | } 21 | -------------------------------------------------------------------------------- /examples/src/Component.svelte: -------------------------------------------------------------------------------- 1 | 16 | 17 |
18 |

Hello {name}!

19 | 22 | 25 |
26 | 27 | -------------------------------------------------------------------------------- /packages/cli/src/commands/doc.ts: -------------------------------------------------------------------------------- 1 | import execa from "execa"; 2 | import { logger } from "../helpers/logger"; 3 | 4 | export async function command(commandOptions: any) { 5 | try { 6 | if (commandOptions.debug) { 7 | logger.level = "debug"; 8 | } 9 | if (commandOptions.build) { 10 | logger.info("Building storybook to static files..."); 11 | const subprocess = execa("npx", ["build-storybook"]); 12 | subprocess.stdout.pipe(process.stdout); 13 | await (async () => { 14 | await subprocess; 15 | logger.info("Built storybook to static files successfully"); 16 | })(); 17 | } else { 18 | logger.info("Start storybook for component"); 19 | await execa("npx", ["start-storybook", "-p", commandOptions.port], { 20 | cwd: "." 21 | }).stdout.pipe(process.stdout); 22 | } 23 | } catch (error) { 24 | logger.error(error); 25 | process.exit(1); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /packages/core/src/vue/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "rootDirs": [".","../"], 4 | "outDir": "dist", 5 | "module": "esnext", 6 | "target": "es5", 7 | "skipDefaultLibCheck": true, 8 | "allowJs": true, 9 | "esModuleInterop": true, 10 | "lib": ["esnext", "dom"], 11 | "baseUrl": ".", 12 | "resolveJsonModule": true, 13 | "allowSyntheticDefaultImports": true, 14 | "moduleResolution": "node", 15 | "forceConsistentCasingInFileNames": true, 16 | "noImplicitReturns": true, 17 | "suppressImplicitAnyIndexErrors": true, 18 | "noUnusedLocals": true, 19 | "experimentalDecorators": true, 20 | "strict": true, 21 | "skipLibCheck": true, 22 | "typeRoots": ["./node_modules/@types","../types"] 23 | }, 24 | "exclude": [ 25 | "node_modules", 26 | "jest", 27 | "dist", 28 | ], 29 | "include": ["./**/*","../types/**/*", 30 | "../helpers/**/*" 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /packages/core/src/vue3/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "rootDirs": [".","../"], 4 | "outDir": "dist", 5 | "module": "esnext", 6 | "target": "es5", 7 | "skipDefaultLibCheck": true, 8 | "allowJs": true, 9 | "esModuleInterop": true, 10 | "lib": ["esnext", "dom"], 11 | "baseUrl": ".", 12 | "resolveJsonModule": true, 13 | "allowSyntheticDefaultImports": true, 14 | "moduleResolution": "node", 15 | "forceConsistentCasingInFileNames": true, 16 | "noImplicitReturns": true, 17 | "suppressImplicitAnyIndexErrors": true, 18 | "noUnusedLocals": true, 19 | "experimentalDecorators": true, 20 | "strict": true, 21 | "skipLibCheck": true, 22 | "typeRoots": ["./node_modules/@types","../types"] 23 | }, 24 | "exclude": [ 25 | "node_modules", 26 | "jest", 27 | "dist", 28 | ], 29 | "include": ["./**/*","../types/**/*", 30 | "../helpers/**/*" 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /packages/cli/src/commands/test.ts: -------------------------------------------------------------------------------- 1 | import execa from "execa"; 2 | import { logger } from "../helpers/logger"; 3 | 4 | export async function command(commandOptions: any) { 5 | try { 6 | if (commandOptions.debug) { 7 | logger.level = "debug"; 8 | } 9 | if (commandOptions.open) { 10 | logger.info("Run cypress open"); 11 | await execa("npx", ["cypress", "open"], { 12 | cwd: "." 13 | }).stdout.pipe(process.stdout); 14 | } else { 15 | logger.info("Run cypress and jest test runner"); 16 | await execa( 17 | "npx", 18 | [ 19 | "start-server-and-test", 20 | "start", 21 | `http://0.0.0.0:${commandOptions.port}`, 22 | "cypress run" 23 | ], 24 | { 25 | cwd: "." 26 | } 27 | ).stdout.pipe(process.stdout); 28 | } 29 | } catch (error) { 30 | logger.error(error); 31 | process.exit(1); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /packages/core/src/react/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "rootDirs": [".","../"], 4 | "outDir": "dist", 5 | "module": "esnext", 6 | "target": "es5", 7 | "skipDefaultLibCheck": true, 8 | "allowJs": true, 9 | "esModuleInterop": true, 10 | "lib": ["esnext", "dom"], 11 | "baseUrl": ".", 12 | "jsx": "react", 13 | "resolveJsonModule": true, 14 | "allowSyntheticDefaultImports": true, 15 | "moduleResolution": "node", 16 | "forceConsistentCasingInFileNames": true, 17 | "noImplicitReturns": true, 18 | "suppressImplicitAnyIndexErrors": true, 19 | "noUnusedLocals": true, 20 | "experimentalDecorators": true, 21 | "strict": true, 22 | "skipLibCheck": true, 23 | "typeRoots": ["./node_modules/@types","../types"] 24 | }, 25 | "exclude": [ 26 | "node_modules", 27 | "jest", 28 | "dist" 29 | ], 30 | "include": ["./**/*","../types/**/*", 31 | "../helpers/**/*" 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /packages/core/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "rootDir": ".", 5 | "outDir": "dist", 6 | "module": "esnext", 7 | "target": "es5", 8 | "skipDefaultLibCheck": true, 9 | "allowJs": true, 10 | "esModuleInterop": true, 11 | "lib": ["esnext", "dom"], 12 | "baseUrl": ".", 13 | "jsx": "react", 14 | "resolveJsonModule": true, 15 | "allowSyntheticDefaultImports": true, 16 | "moduleResolution": "node", 17 | "forceConsistentCasingInFileNames": true, 18 | "noImplicitReturns": true, 19 | "suppressImplicitAnyIndexErrors": true, 20 | "noUnusedLocals": true, 21 | "experimentalDecorators": true, 22 | "strict": true, 23 | "skipLibCheck": true, 24 | "typeRoots": ["./node_modules/@types"], 25 | }, 26 | "exclude": [ 27 | "node_modules", 28 | "jest", 29 | "dist", 30 | ], 31 | "include": [ 32 | "**/*.ts", 33 | "src/types/index.d.ts" 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /packages/cli/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | ## [1.0.6](https://github.com/FE-PIRL/svere/compare/@svere/cli@1.0.4...@svere/cli@1.0.6) (2022-01-25) 7 | 8 | **Note:** Version bump only for package @svere/cli 9 | 10 | 11 | 12 | 13 | 14 | ## [1.0.4](https://github.com/FE-PIRL/svere/compare/@svere/cli@1.0.1...@svere/cli@1.0.4) (2022-01-25) 15 | 16 | 17 | ### Bug Fixes 18 | 19 | * read package.json path uncorrect ([457bf1e](https://github.com/FE-PIRL/svere/commit/457bf1eb1506852fec9ecf0d3b3eb199a8c51100)) 20 | * update kleur in dependencies ([3b6127b](https://github.com/FE-PIRL/svere/commit/3b6127bec11d4c50426c23333ef0152788f7f307)) 21 | 22 | 23 | 24 | ## 1.0.3 (2021-06-04) 25 | 26 | 27 | 28 | 29 | 30 | ## 1.0.0 (2021-05-19) 31 | 32 | **Note:** First release of @svere/cli, with create cmd only 33 | 34 | ## 1.0.0 (2021-05-24) 35 | 36 | **Note:** add build cmd 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Productivity Improvement Research Laboratory 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/core/dist/vue.mjs: -------------------------------------------------------------------------------- 1 | import t from"vue";var n=function(){return n=Object.assign||function(t){for(var n,e=1,r=arguments.length;e { 20 | const bundle = await rollup(inputOptions); 21 | await bundle.write(inputOptions.output); 22 | } 23 | ) 24 | .catch((e: any) => { 25 | throw e; 26 | }); 27 | logger.info("Build component successfully"); 28 | await promise; 29 | } catch (error) { 30 | logger.error(error); 31 | process.exit(1); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /examples/src/__tests__/Component.test.ts: -------------------------------------------------------------------------------- 1 | import { render, fireEvent } from "@testing-library/svelte"; 2 | import App from "../Component.svelte"; 3 | 4 | test("should render", () => { 5 | const results = render(App, { props: { name: "world" } }); 6 | 7 | expect(() => results.getByText("Hello world!")).not.toThrow(); 8 | }); 9 | 10 | 11 | // Note: This is as an async test as we are using `fireEvent` 12 | test('count increments when button is clicked', async () => { 13 | const results = render(App, { props: { name: "world" } }); 14 | const button = results.getByText('add count: 0'); 15 | 16 | // Using await when firing events is unique to the svelte testing library because 17 | // we have to wait for the next `tick` so that Svelte flushes all pending state changes. 18 | await fireEvent.click(button); 19 | 20 | expect(button).toHaveTextContent('add count: 1'); 21 | }) 22 | 23 | 24 | test('listen custom event', async () => { 25 | const results = render(App, { props: { name: "world" } }); 26 | const button = results.getByText('add count: 0'); 27 | 28 | results.component.$on('someEvent', e=>{ 29 | expect(e.detail).toBe(1); 30 | }) 31 | 32 | await fireEvent.click(button); 33 | }) -------------------------------------------------------------------------------- /packages/core/src/react/__tests__/__snapshots__/React.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`React custom wrapper 1`] = ` 4 |
5 |
10 |
13 |

16 | Hello 17 | ben 18 | ! 19 |

20 | 21 | 25 | 26 | 30 |
31 |
32 |
33 | `; 34 | 35 | exports[`React should render 1`] = ` 36 |
37 |
40 |
43 |

46 | Hello 47 | ben 48 | ! 49 |

50 | 51 | 55 | 56 | 60 |
61 |
62 |
63 | `; 64 | -------------------------------------------------------------------------------- /packages/core/src/vue/rollup.config.js: -------------------------------------------------------------------------------- 1 | import resolve from '@rollup/plugin-node-resolve'; 2 | import pkg from './package.json'; 3 | import typescript from '@rollup/plugin-typescript'; 4 | import commonjs from '@rollup/plugin-commonjs'; 5 | import { terser } from 'rollup-plugin-terser'; 6 | 7 | const name = pkg.name 8 | .replace(/^(@\S+\/)?(svelte-)?(\S+)/, '$3') 9 | .replace(/^\w/, m => m.toUpperCase()) 10 | .replace(/-\w/g, m => m[1].toUpperCase()); 11 | const production = !process.env.ROLLUP_WATCH; 12 | 13 | export default { 14 | input: ['./index.ts'], 15 | output: [ 16 | { file: pkg.module, 'format': 'es', globals: { "vue": "Vue" } }, 17 | { file: pkg.main, 'format': 'umd', name, globals: { "vue": "Vue" } }, 18 | ], 19 | external: ["vue"], 20 | plugins: [ 21 | typescript({ 22 | sourceMap: !production, 23 | types: ["../types"], 24 | include: [ 25 | './**/*.ts', 26 | '../helpers/**/*.ts' 27 | ], 28 | exclude:["./__tests__/**/*"] 29 | }), 30 | commonjs({ 31 | include: /node_modules/ 32 | }), 33 | resolve({ 34 | browser: true 35 | }), 36 | // If we're building for production (npm run build 37 | // instead of npm run dev), minify 38 | production && terser({ format: { comments: false } }) 39 | ], 40 | }; 41 | -------------------------------------------------------------------------------- /packages/core/src/vue3/rollup.config.js: -------------------------------------------------------------------------------- 1 | import resolve from '@rollup/plugin-node-resolve'; 2 | import pkg from './package.json'; 3 | import typescript from '@rollup/plugin-typescript'; 4 | import commonjs from '@rollup/plugin-commonjs'; 5 | import { terser } from 'rollup-plugin-terser'; 6 | 7 | const name = pkg.name 8 | .replace(/^(@\S+\/)?(svelte-)?(\S+)/, '$3') 9 | .replace(/^\w/, m => m.toUpperCase()) 10 | .replace(/-\w/g, m => m[1].toUpperCase()); 11 | const production = !process.env.ROLLUP_WATCH; 12 | 13 | export default { 14 | input: ['./index.ts'], 15 | output: [ 16 | { file: pkg.module, 'format': 'es',globals: { "vue": "Vue" } }, 17 | { file: pkg.main, 'format': 'umd', name,globals: { "vue": "Vue" } }, 18 | ], 19 | external: ["vue"], 20 | plugins: [ 21 | typescript({ 22 | sourceMap: !production, 23 | types: ["../types"], 24 | include: [ 25 | './**/*.ts', 26 | '../helpers/**/*.ts' 27 | ], 28 | exclude:["./__tests__/**/*"] 29 | }), 30 | commonjs({ 31 | include: /node_modules/ 32 | }), 33 | resolve({ 34 | browser: true 35 | }), 36 | // If we're building for production (npm run build 37 | // instead of npm run dev), minify 38 | production && terser({ format: { comments: false } }) 39 | ], 40 | }; 41 | -------------------------------------------------------------------------------- /packages/core/src/react/rollup.config.js: -------------------------------------------------------------------------------- 1 | import resolve from '@rollup/plugin-node-resolve'; 2 | import pkg from './package.json'; 3 | import typescript from '@rollup/plugin-typescript'; 4 | import commonjs from '@rollup/plugin-commonjs'; 5 | import { terser } from 'rollup-plugin-terser'; 6 | 7 | const name = pkg.name 8 | .replace(/^(@\S+\/)?(svelte-)?(\S+)/, '$3') 9 | .replace(/^\w/, m => m.toUpperCase()) 10 | .replace(/-\w/g, m => m[1].toUpperCase()); 11 | const production = !process.env.ROLLUP_WATCH; 12 | 13 | export default { 14 | input: ['./index.ts'], 15 | output: [ 16 | { file: pkg.module, 'format': 'es',globals: { "react": "React" } }, 17 | { file: pkg.main, 'format': 'umd', name,globals: { "react": "React" } }, 18 | ], 19 | external: ["react"], 20 | plugins: [ 21 | typescript({ 22 | sourceMap: !production, 23 | types: ["../types"], 24 | include: [ 25 | './**/*.ts', 26 | '../helpers/**/*.ts' 27 | ], 28 | exclude:["./__tests__/**/*"] 29 | }), 30 | commonjs({ 31 | include: /node_modules/ 32 | }), 33 | resolve({ 34 | browser: true 35 | }), 36 | // If we're building for production (npm run build 37 | // instead of npm run dev), minify 38 | production && terser({ format: { comments: false } }) 39 | ], 40 | }; 41 | -------------------------------------------------------------------------------- /packages/core/src/vue3/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "serve-vue3", 3 | "version": "1.0.0", 4 | "description": "svelte adapter for vue3", 5 | "module": "../../dist/vue3.mjs", 6 | "main": "../../dist/vue3.js", 7 | "scripts": { 8 | "test": "jest", 9 | "build": "rollup -c", 10 | "prettier": "prettier --write --loglevel warn \"src/**/*.{js,json,ts,tsx}\"" 11 | }, 12 | "keywords": [], 13 | "author": "wangbingying", 14 | "license": "ISC", 15 | "dependencies": { 16 | "@vue/compiler-sfc": "^3.0.11", 17 | "ts-node": "^9.1.1", 18 | "typescript": "^4.2.4", 19 | "vue": "^3.0.11" 20 | }, 21 | "devDependencies": { 22 | "@jest/reporters": "^26.6.2", 23 | "@rollup/plugin-commonjs": "^18.0.0", 24 | "@rollup/plugin-node-resolve": "^8.0.0", 25 | "@rollup/plugin-typescript": "^8.2.1", 26 | "@testing-library/jest-dom": "^5.11.4", 27 | "@types/jest": "^26.0.22", 28 | "@vue/test-utils": "^2.0.0-rc.6", 29 | "jest": "^26.6.3", 30 | "prettier": "^2.2.1", 31 | "rollup": "^2.3.4", 32 | "rollup-plugin-terser": "^7.0.2", 33 | "svelte": "^3.0.0", 34 | "svelte-check": "^1.0.0", 35 | "svelte-jester": "^1.1.5", 36 | "svelte-preprocess": "^4.0.0", 37 | "ts-jest": "^26.5.5", 38 | "tslib": "^2.2.0" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /packages/core/src/vue/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "serve-vue", 3 | "version": "1.0.0", 4 | "description": "svelte adapter for vue", 5 | "module": "../../dist/vue.mjs", 6 | "main": "../../dist/vue.js", 7 | "scripts": { 8 | "test": "jest", 9 | "build": "rollup -c", 10 | "prettier": "prettier --write --loglevel warn \"src/**/*.{js,json,ts,tsx}\"" 11 | }, 12 | "keywords": [], 13 | "author": "wangbingying", 14 | "license": "ISC", 15 | "dependencies": { 16 | "ts-node": "^9.1.1", 17 | "typescript": "^4.2.4", 18 | "vue": "^2.6.12", 19 | "vue-template-compiler": "^2.6.12" 20 | }, 21 | "devDependencies": { 22 | "@jest/reporters": "^26.6.2", 23 | "@rollup/plugin-commonjs": "^18.0.0", 24 | "@rollup/plugin-node-resolve": "^8.0.0", 25 | "@rollup/plugin-typescript": "^8.2.1", 26 | "@testing-library/jest-dom": "^5.11.4", 27 | "@types/jest": "^26.0.22", 28 | "@types/vue": "^2.0.0", 29 | "@vue/test-utils": "^1.1.4", 30 | "jest": "^26.6.3", 31 | "prettier": "^2.2.1", 32 | "rollup": "^2.3.4", 33 | "rollup-plugin-terser": "^7.0.2", 34 | "svelte": "^3.0.0", 35 | "svelte-check": "^1.0.0", 36 | "svelte-jester": "^1.1.5", 37 | "svelte-preprocess": "^4.0.0", 38 | "ts-jest": "^26.5.5", 39 | "tslib": "^2.2.0" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /packages/core/src/react/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "serve-react", 3 | "version": "1.0.0", 4 | "description": "svelte adapter for react", 5 | "module": "../../dist/react.mjs", 6 | "main": "../../dist/react.js", 7 | "scripts": { 8 | "test": "jest", 9 | "build": "rollup -c", 10 | "prettier": "prettier --write --loglevel warn \"src/**/*.{js,json,ts,tsx}\"" 11 | }, 12 | "keywords": [], 13 | "author": "wangbingying", 14 | "license": "ISC", 15 | "dependencies": { 16 | "react": "^17.0.2", 17 | "react-dom": "^17.0.2", 18 | "ts-node": "^9.1.1", 19 | "typescript": "^4.2.4" 20 | }, 21 | "devDependencies": { 22 | "@jest/reporters": "^26.6.2", 23 | "@rollup/plugin-commonjs": "^18.0.0", 24 | "@rollup/plugin-node-resolve": "^8.0.0", 25 | "@rollup/plugin-typescript": "^8.2.1", 26 | "@testing-library/jest-dom": "^5.11.4", 27 | "@types/jest": "^26.0.22", 28 | "@types/react": "^17.0.3", 29 | "@types/react-dom": "^17.0.3", 30 | "jest": "^26.6.3", 31 | "prettier": "^2.2.1", 32 | "rollup": "^2.3.4", 33 | "rollup-plugin-terser": "^7.0.2", 34 | "svelte": "^3.0.0", 35 | "svelte-check": "^1.0.0", 36 | "svelte-jester": "^1.1.5", 37 | "svelte-preprocess": "^4.0.0", 38 | "ts-jest": "^26.5.5", 39 | "tslib": "^2.2.0" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /examples/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "examples", 3 | "version": "1.0.0", 4 | "svelte": "src/index.js", 5 | "module": "dist/index.mjs", 6 | "main": "dist/index.js", 7 | "scripts": { 8 | "build": "rollup -c", 9 | "validate": "svelte-check", 10 | "test": "jest" 11 | }, 12 | "devDependencies": { 13 | "@rollup/plugin-commonjs": "^14.0.0", 14 | "@rollup/plugin-node-resolve": "^8.0.0", 15 | "@rollup/plugin-typescript": "^8.2.1", 16 | "@testing-library/jest-dom": "^5.11.4", 17 | "@testing-library/svelte": "^3.0.3", 18 | "@tsconfig/svelte": "^1.0.0", 19 | "@types/jest": "^26.0.14", 20 | "jest": "^26.6.3", 21 | "rollup": "^2.3.4", 22 | "rollup-plugin-svelte": "^6.0.0", 23 | "svelte": "^3.37.0", 24 | "svelte-check": "^1.0.0", 25 | "svelte-jester": "^1.5.0", 26 | "svelte-preprocess": "^4.0.0", 27 | "ts-jest": "^26.5.5", 28 | "tslib": "^2.2.0", 29 | "typescript": "^4.2.4" 30 | }, 31 | "jest": { 32 | "transform": { 33 | "^.+\\.svelte$": [ 34 | "svelte-jester", 35 | { 36 | "preprocess": true 37 | } 38 | ], 39 | "^.+\\.ts$": "ts-jest" 40 | }, 41 | "moduleFileExtensions": [ 42 | "js", 43 | "ts", 44 | "svelte" 45 | ], 46 | "setupFilesAfterEnv": [ 47 | "@testing-library/jest-dom/extend-expect" 48 | ] 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /packages/core/dist/react.mjs: -------------------------------------------------------------------------------- 1 | import r,{useRef as n,useState as t,useEffect as e}from"react";var o=function(){return o=Object.assign||function(r){for(var n,t=1,e=arguments.length;t { 25 | const watcher = await watch(inputOptions); 26 | watcher.on("change", (id, e) => { 27 | logger.debug(id + " [" + e.event + "] "); 28 | return e; 29 | }); 30 | watcher.close(); 31 | return watcher; 32 | } 33 | ) 34 | .catch((e: any) => { 35 | throw e; 36 | }); 37 | logger.info("Start application successfully"); 38 | await promise; 39 | } catch (error) { 40 | logger.error(error); 41 | process.exit(1); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /packages/core/dist/vue3.js: -------------------------------------------------------------------------------- 1 | !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t(require("vue")):"function"==typeof define&&define.amd?define(["vue"],t):(e="undefined"!=typeof globalThis?globalThis:e||self).ServeVue3=t(e.Vue)}(this,(function(e){"use strict";var t=function(){return t=Object.assign||function(e){for(var t,n=1,r=arguments.length;n> { 22 | const allInputs = concatAllArray( 23 | opts.input.map((input: string) => 24 | createAllFormats(opts, input).map( 25 | (options: SvereOptions, index: number) => ({ 26 | ...options, 27 | // We want to know if this is the first run for each entryfile 28 | // for certain plugins (e.g. css) 29 | writeMeta: index === 0 30 | }) 31 | ) 32 | ) 33 | ); 34 | 35 | return await Promise.all( 36 | allInputs.map(async (options: SvereOptions, index: number) => { 37 | // pass the full rollup config to svere.config.js override 38 | const config = await createRollupConfig(options, index); 39 | return svereConfig.rollup(config, options); 40 | }) 41 | ); 42 | } 43 | 44 | function createAllFormats( 45 | opts: NormalizedOpts, 46 | input: string 47 | ): [SvereOptions, ...SvereOptions[]] { 48 | return [ 49 | opts.format.includes("esm") && { ...opts, format: "esm", input }, 50 | opts.format.includes("iife") && { ...opts, format: "iife", input }, 51 | opts.format.includes("umd") && { 52 | ...opts, 53 | format: "umd", 54 | env: "development", 55 | input 56 | }, 57 | opts.format.includes("umd") && { 58 | ...opts, 59 | format: "umd", 60 | env: "production", 61 | input 62 | } 63 | ].filter(Boolean) as [SvereOptions, ...SvereOptions[]]; 64 | } 65 | -------------------------------------------------------------------------------- /packages/core/src/react/index.ts: -------------------------------------------------------------------------------- 1 | import React, { useRef, useEffect, useState } from "react"; 2 | import resolveWrapperProps from "../helpers"; 3 | 4 | export default (Component: SvelteComponentConstructor, _wrapperProps?: WrapperProps) => (props: { 5 | [x: string]: any; 6 | }) => { 7 | const container = useRef(null); 8 | const component = useRef(null); 9 | 10 | const [mounted, setMount] = useState(false); 11 | 12 | useEffect(() => { 13 | const onRegex = /on([A-Z]+[a-zA-Z]*)/; 14 | const watchRegex = /watch([A-Z]+[a-zA-Z]*)/; 15 | 16 | component.current = new Component({ target: container.current, props }); 17 | 18 | let watchers: any[][] = []; 19 | for (const key in props) { 20 | const onMatch = key.match(onRegex); 21 | const watchMatch = key.match(watchRegex); 22 | 23 | if (onMatch && typeof props[key] === "function") { 24 | ((component.current as unknown) as SvelteComponent)?.$on( 25 | `${onMatch[1][0].toLowerCase()}${onMatch[1].slice(1)}`, 26 | props[key] 27 | ); 28 | } 29 | 30 | if (watchMatch && typeof props[key] === "function") { 31 | watchers.push([ 32 | `${watchMatch[1][0].toLowerCase()}${watchMatch[1].slice(1)}`, 33 | props[key], 34 | ]); 35 | } 36 | } 37 | 38 | if (watchers.length) { 39 | if (component.current?.$$.update) { 40 | const update = component.current?.$$.update; 41 | component.current.$$.update = function () { 42 | watchers.forEach(([name, callback]) => { 43 | const index = component.current?.$$.props[name]; 44 | const prop = component.current?.$$.ctx[index]; 45 | prop && callback(prop); 46 | }); 47 | update.apply(null, arguments); 48 | }; 49 | } 50 | } 51 | 52 | return () => component.current?.$destroy(); 53 | }, []); 54 | 55 | useEffect(() => { 56 | if (!mounted) { 57 | setMount(true); 58 | return; 59 | } 60 | 61 | component.current?.$set(props); 62 | }, [props]); 63 | 64 | const wrapperProps = resolveWrapperProps(_wrapperProps); 65 | 66 | return React.createElement(wrapperProps.element, { 67 | ref: container, 68 | ...wrapperProps 69 | }); 70 | }; 71 | -------------------------------------------------------------------------------- /packages/cli/src/types/index.ts: -------------------------------------------------------------------------------- 1 | export type LoggerLevel = "debug" | "info" | "warn" | "error" | "silent"; // same as Pino 2 | export type LoggerEvent = "debug" | "info" | "warn" | "error"; 3 | export interface LoggerOptions { 4 | /** (optional) change name at beginning of line */ 5 | name?: string; 6 | /** (optional) do some additional work after logging a message, if log level is enabled */ 7 | task?: Function; 8 | } 9 | export interface PackageJson { 10 | name: string; 11 | version: string; 12 | author: string; 13 | repository: any; 14 | } 15 | 16 | interface SharedOpts { 17 | commandName?: string; 18 | // JS target 19 | target: "node" | "browser"; 20 | // Path to tsconfig file 21 | tsconfig?: string; 22 | // Is error extraction running? 23 | extractErrors?: boolean; 24 | } 25 | 26 | export type ModuleFormat = "cjs" | "umd" | "esm" | "iife"; 27 | 28 | export interface BuildOpts extends SharedOpts { 29 | fileName?: string; 30 | entry?: string | string[]; 31 | format: "umd,esm"; 32 | target: "browser"; 33 | } 34 | 35 | export interface WatchOpts extends BuildOpts { 36 | verbose?: boolean; 37 | noClean?: boolean; 38 | // callback hooks 39 | onFirstSuccess?: string; 40 | onSuccess?: string; 41 | onFailure?: string; 42 | } 43 | 44 | export interface NormalizedOpts 45 | extends Omit { 46 | name: string; 47 | input: string[]; 48 | format: [ModuleFormat, ...ModuleFormat[]]; 49 | } 50 | 51 | export interface SvereOptions extends SharedOpts { 52 | // Name of package 53 | name: string; 54 | // Port for dev 55 | port: number; 56 | // never launch browser automatically 57 | silent: boolean; 58 | // path to file 59 | input: string; 60 | // Environment 61 | env: "development" | "production"; 62 | // Module format 63 | format: ModuleFormat; 64 | // Is minifying? 65 | minify?: boolean; 66 | // Is this the very first rollup config (and thus should one-off metadata be extracted)? 67 | writeMeta?: boolean; 68 | // Only transpile, do not type check (makes compilation faster) 69 | transpileOnly?: boolean; 70 | } 71 | 72 | export interface PackageJson { 73 | name: string; 74 | source?: string; 75 | jest?: any; 76 | eslint?: any; 77 | dependencies?: { [packageName: string]: string }; 78 | devDependencies?: { [packageName: string]: string }; 79 | engines?: { 80 | node?: string; 81 | }; 82 | } 83 | -------------------------------------------------------------------------------- /packages/core/src/vue/index.ts: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import resolveWrapperProps from "../helpers"; 3 | 4 | export default (Component: any, wrapperProps?: WrapperProps) => { 5 | wrapperProps = resolveWrapperProps(wrapperProps); 6 | 7 | return Vue.component(wrapperProps.id as string, { 8 | render(createElement) { 9 | return createElement(wrapperProps?.element, { 10 | ref: "container", 11 | props: this.$attrs, 12 | class: wrapperProps?.className, 13 | attrs: { id: wrapperProps?.id }, 14 | style: { ...wrapperProps?.styles }, 15 | }); 16 | }, 17 | data() { 18 | return { 19 | comp: null, 20 | }; 21 | }, 22 | mounted() { 23 | // @ts-ignore 24 | this.comp = new Component({ 25 | target: this.$refs.container, 26 | props: this.$attrs, 27 | }); 28 | 29 | let watchers: any[][] = []; 30 | 31 | for (const key in this.$listeners) { 32 | (this as any).comp.$on(key, this.$listeners[key]); 33 | 34 | const watchRe = /watch:([^]+)/; 35 | const watchMatch = key.match(watchRe); 36 | 37 | if (watchMatch && typeof this.$listeners[key] === "function") { 38 | watchers.push([ 39 | `${watchMatch[1][0].toLowerCase()}${watchMatch[1].slice(1)}`, 40 | this.$listeners[key], 41 | ]); 42 | } 43 | } 44 | 45 | if (watchers.length) { 46 | let comp = (this as any).comp; 47 | const update = (this as any).comp.$$.update; 48 | 49 | (this as any).comp.$$.update = function () { 50 | watchers.forEach(([name, callback]) => { 51 | const index = comp.$$.props[name]; 52 | const prop = comp.$$.ctx[index]; 53 | prop && callback(prop); 54 | }); 55 | update.apply(null, arguments); 56 | }; 57 | } 58 | }, 59 | updated() { 60 | (this as any).comp.$set(this.$attrs); 61 | }, 62 | destroyed() { 63 | (this as any).comp.$destroy(); 64 | }, 65 | }); 66 | }; 67 | -------------------------------------------------------------------------------- /packages/cli/__tests__/cli.test.ts: -------------------------------------------------------------------------------- 1 | import { filesystem } from "gluegun"; 2 | const fs = require("fs-extra"); 3 | const { version } = require("../package.json"); 4 | const exec = require("child_process").exec; 5 | const root = filesystem.path(__dirname, ".."); 6 | const script = filesystem.path(root, "bin", "svere-cli"); 7 | 8 | function cli(args, cwd) { 9 | return new Promise(resolve => { 10 | exec( 11 | `node ${script} ${args.join(" ")}`, 12 | { cwd }, 13 | (error, stdout, stderr) => { 14 | resolve({ 15 | code: error && error.code ? error.code : 0, 16 | error, 17 | stdout, 18 | stderr 19 | }); 20 | } 21 | ); 22 | }); 23 | } 24 | 25 | test("outputs version", async () => { 26 | const output = await cli(["--version"], "."); 27 | // @ts-ignore 28 | expect(output.stdout).toContain(version); 29 | }); 30 | 31 | test("create from template", async () => { 32 | jest.setTimeout(80 * 1000); 33 | 34 | const builder = await cli(["create test-install -d -f -si"], "."); 35 | console.log(builder); 36 | 37 | // rollup.config.js is a file we can test for to assume successful 38 | // install, since it’s added at the end. 39 | const rollupConfig = filesystem.path( 40 | root, 41 | "test-install", 42 | "rollup.config.js" 43 | ); 44 | const rollupConfigExists = fs.existsSync(rollupConfig); 45 | expect(rollupConfigExists).toBe(true); 46 | 47 | // install node_modules by default 48 | const modules = filesystem.path(root, "test-install", "node_modules"); 49 | const modulesExist = fs.existsSync(modules); 50 | expect(modulesExist).toBe(false); 51 | }); 52 | 53 | test("build component", async () => { 54 | jest.setTimeout(80 * 1000); 55 | 56 | // assume you had run test case above and executed npm install 57 | const result = await cli(["build --fileName test"], "./test-install"); 58 | //console.log(result); 59 | const dist = filesystem.path(root, "test-install", "dist"); 60 | const distExists = fs.existsSync(dist); 61 | expect(distExists).toBe(true); 62 | 63 | const testFile = filesystem.path(root, "test-install", "dist", "test.esm.js"); 64 | const testFileExists = fs.existsSync(testFile); 65 | expect(testFileExists).toBe(true); 66 | }); 67 | 68 | test("start dev server", async () => { 69 | jest.setTimeout(80 * 1000); 70 | 71 | // assume you had run test case above and executed npm install 72 | const devServer = await cli(["start -d"], "./test-install"); 73 | console.log(devServer); 74 | }); 75 | -------------------------------------------------------------------------------- /packages/core/src/vue3/__tests__/Vue.test.tsx: -------------------------------------------------------------------------------- 1 | import { mount } from "@vue/test-utils"; 2 | 3 | import toVue3 from "../index"; 4 | import SvelteComponent from "../../../../../examples/dist"; 5 | 6 | describe("Vue", () => { 7 | let container: any; 8 | const name = "ben"; 9 | 10 | beforeEach(() => { 11 | container = document.createElement("div"); 12 | document.body.appendChild(container); 13 | }); 14 | 15 | afterEach(() => { 16 | document.body.removeChild(container); 17 | container = null; 18 | }); 19 | 20 | it("should render", async () => { 21 | const VueComponent = toVue3(SvelteComponent); 22 | 23 | const wrapper = mount(VueComponent, { props: { name } }); 24 | 25 | expect(wrapper.html()).toMatchSnapshot(); 26 | }); 27 | 28 | it("count increments when button is clicked", async () => { 29 | const VueComponent = toVue3(SvelteComponent); 30 | 31 | const wrapper = mount(VueComponent, { props: { name } }); 32 | expect(wrapper.find("button").text()).toBe( "add count: 0"); 33 | 34 | await wrapper.find("button").trigger("click"); 35 | 36 | expect(wrapper.find("button").text()).toBe("add count: 1"); 37 | }); 38 | 39 | //listeners has not been supported for now 40 | /* it("watch internal props changes and execute the callback", async () => { 41 | const VueComponent = toVue3(SvelteComponent); 42 | 43 | const handleSomeEvent = (name: string) => { 44 | console.log(name) 45 | expect(name).toBe("yasin"); 46 | }; 47 | const wrapper = mount(VueComponent, { 48 | listeners: { 49 | "watch:name": handleSomeEvent, 50 | }, 51 | }); 52 | 53 | await wrapper.find("button").trigger("click"); 54 | }); 55 | 56 | it("listen custom event", async () => { 57 | const VueComponent = toVue3(SvelteComponent); 58 | 59 | const handleSomeEvent = (e: any) => { 60 | console.log(e.detail); 61 | expect(e.detail).toBe(1); 62 | }; 63 | 64 | const wrapper = mount(VueComponent, { 65 | listeners: { 66 | someEvent: handleSomeEvent, 67 | }, 68 | }); 69 | 70 | await wrapper.find("button").trigger("click"); 71 | });*/ 72 | 73 | it("custom wrapper", async () => { 74 | const wrapperProps = { 75 | element: "section", 76 | className: "section-css", 77 | id: "svelte-vue3", 78 | styles: { 79 | border: "1px solid gray", 80 | }, 81 | }; 82 | const VueComponent = toVue3(SvelteComponent, wrapperProps); 83 | 84 | const wrapper = mount(VueComponent, { props: { name } }); 85 | 86 | expect(wrapper.html()).toMatchSnapshot(); 87 | }); 88 | }); 89 | -------------------------------------------------------------------------------- /packages/core/src/vue/__tests__/Vue.test.tsx: -------------------------------------------------------------------------------- 1 | import { shallowMount } from "@vue/test-utils"; 2 | 3 | import toVue from "../index"; 4 | import SvelteComponent from "../../../../../examples/dist"; 5 | 6 | describe("Vue", () => { 7 | let container: any; 8 | let name = "ben"; 9 | 10 | beforeEach(() => { 11 | container = document.createElement("div"); 12 | document.body.appendChild(container); 13 | }); 14 | 15 | afterEach(() => { 16 | document.body.removeChild(container); 17 | container = null; 18 | }); 19 | 20 | it("should render", async () => { 21 | const VueComponent = toVue(SvelteComponent); 22 | 23 | const wrapper = shallowMount(VueComponent,{propsData: { name }}); 24 | 25 | expect(wrapper.html()).toMatchSnapshot(); 26 | }); 27 | 28 | it("count increments when button is clicked", async () => { 29 | const VueComponent = toVue(SvelteComponent); 30 | 31 | const wrapper = shallowMount(VueComponent,{propsData: { name }}); 32 | expect(wrapper.find("button").text()).toBe("add count: 0"); 33 | 34 | await wrapper.find("button").trigger("click"); 35 | 36 | expect(wrapper.find("button").text()).toBe("add count: 1"); 37 | }); 38 | 39 | it("watch internal props changes and execute the callback", async () => { 40 | const VueComponent = toVue(SvelteComponent); 41 | 42 | const handleSomeEvent = (name: string) => { 43 | //console.log(name) 44 | expect(name).toBe("yasin"); 45 | }; 46 | const wrapper = shallowMount(VueComponent, { 47 | propsData: { name }, 48 | listeners: { 49 | "watch:name": handleSomeEvent, 50 | }, 51 | }); 52 | 53 | await wrapper.find("button").trigger("click"); 54 | }); 55 | 56 | it("listen custom event", async () => { 57 | const VueComponent = toVue(SvelteComponent); 58 | 59 | const handleSomeEvent = (e: any) => { 60 | //console.log(e.detail); 61 | expect(e.detail).toBe(1); 62 | }; 63 | 64 | const wrapper = shallowMount(VueComponent, { 65 | propsData: { name }, 66 | listeners: { 67 | someEvent: handleSomeEvent, 68 | }, 69 | }); 70 | 71 | await wrapper.find("button").trigger("click"); 72 | }); 73 | 74 | it("custom wrapper", async () => { 75 | const wrapperProps = { 76 | element: "section", 77 | className: "section-css", 78 | id: "svelte-vue2", 79 | styles: { 80 | border: "1px solid gray", 81 | }, 82 | }; 83 | const VueComponent = toVue(SvelteComponent, wrapperProps); 84 | 85 | const wrapper = shallowMount(VueComponent,{propsData: { name }}); 86 | 87 | expect(wrapper.html()).toMatchSnapshot(); 88 | }); 89 | }); 90 | -------------------------------------------------------------------------------- /packages/cli/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@svere/cli", 3 | "version": "1.0.6", 4 | "description": "svere CLI", 5 | "types": "build/types/types.d.ts", 6 | "bin": { 7 | "svere": "bin/svere-cli" 8 | }, 9 | "scripts": { 10 | "format": "prettier --write {src,src/**,__tests__}/*.{js,ts,tsx,json}", 11 | "clean-build": "npx tsc --build --clean && rm -rf ./build", 12 | "compile": "npx tsc --build --verbose --pretty", 13 | "start": "npm run compile -- --watch", 14 | "build": "npm run format && npm run clean-build && npm run compile", 15 | "prepublishOnly": "npm run build", 16 | "release:patch": "npm version patch && npm publish", 17 | "test": "npx jest", 18 | "snapupdate": "npx jest --updateSnapshot", 19 | "coverage": "npx jest --coverage" 20 | }, 21 | "files": [ 22 | "tsconfig.json", 23 | "build", 24 | "LICENSE", 25 | "readme.md", 26 | "docs", 27 | "bin" 28 | ], 29 | "author": "benyasin", 30 | "license": "MIT", 31 | "repository": { 32 | "type": "git", 33 | "url": "https://github.com/FE-PIRL/svere.git", 34 | "templates": "https://github.com/FE-PIRL/svere-templates.git" 35 | }, 36 | "dependencies": { 37 | "@rollup/plugin-commonjs": "^19.0.0", 38 | "@rollup/plugin-json": "^4.1.0", 39 | "@rollup/plugin-node-resolve": "^13.0.0", 40 | "@rollup/plugin-replace": "^2.4.2", 41 | "asyncro": "^3.0.0", 42 | "camelcase": "^6.2.0", 43 | "commander": "^6.2.1", 44 | "degit": "^2.8.0", 45 | "fs-extra": "^9.1.0", 46 | "gluegun": "latest", 47 | "jpjs": "^1.2.1", 48 | "kleur": "^4.1.1", 49 | "node-sass": "^6.0.0", 50 | "ora": "^5.4.0", 51 | "rollup": "^2.50.0", 52 | "rollup-plugin-css-only": "^3.1.0", 53 | "rollup-plugin-livereload": "^2.0.0", 54 | "rollup-plugin-sourcemaps": "^0.6.3", 55 | "rollup-plugin-svelte": "^7.1.0", 56 | "rollup-plugin-terser": "^7.0.2", 57 | "rollup-plugin-typescript2": "^0.30.0", 58 | "svelte": "^3.38.2", 59 | "svelte-loader": "^3.1.1", 60 | "svelte-preprocess": "^4.7.3", 61 | "tiny-glob": "^0.2.9" 62 | }, 63 | "devDependencies": { 64 | "@storybook/addon-actions": "6.2.9", 65 | "@storybook/addon-docs": "^6.2.9", 66 | "@storybook/addon-essentials": "6.2.9", 67 | "@storybook/addon-links": "6.2.9", 68 | "@storybook/addon-postcss": "2.0.0", 69 | "@storybook/svelte": "6.2.9", 70 | "@testing-library/cypress": "7.0.6", 71 | "@types/fs-extra": "^9.0.8", 72 | "@types/jest": "^26.0.19", 73 | "@types/node": "^12.7.11", 74 | "@types/yargs-parser": "^20.2.0", 75 | "code-specification-unid": "^1.0.9", 76 | "cypress": "7.2.0", 77 | "execa": "^4.0.3", 78 | "jest": "^26.2.2", 79 | "jest-specific-snapshot": "^4.0.0", 80 | "open": "^8.2.0", 81 | "prettier": "^1.12.1", 82 | "rimraf": "^3.0.0", 83 | "sirv-cli": "^1.0.12", 84 | "start-server-and-test": "1.12.1", 85 | "ts-jest": "^26.5.6", 86 | "ts-node": "^8.4.1", 87 | "typescript": "^4.2.3" 88 | }, 89 | "jest": { 90 | "preset": "ts-jest", 91 | "testEnvironment": "node" 92 | }, 93 | "gitHead": "2153d2978edd62553cfe1351a6233d5e586ed56b" 94 | } 95 | -------------------------------------------------------------------------------- /packages/core/src/vue3/index.ts: -------------------------------------------------------------------------------- 1 | import {defineComponent, h, onDeactivated, onMounted, onUpdated, ref} from "vue"; 2 | import resolveWrapperProps from "../helpers"; 3 | 4 | export default (Component: SvelteComponentConstructor, _wrapperProps?: WrapperProps) => { 5 | const wrapperProps = resolveWrapperProps(_wrapperProps); 6 | 7 | return defineComponent({ 8 | name: wrapperProps.id, 9 | setup(props, ctx) { 10 | const container = ref(null); 11 | const component = ref(null); 12 | 13 | onMounted(() => { 14 | const onRegex = /on([A-Z]+[a-zA-Z]*)/; 15 | const watchRegex = /watch([A-Z]+[a-zA-Z]*)/; 16 | 17 | component.value = new Component({ 18 | target: container.value, 19 | props: ctx.attrs, 20 | }); 21 | 22 | let watchers: any[][] = []; 23 | for (const key in ctx.attrs) { 24 | const onMatch = key.match(onRegex); 25 | const watchMatch = key.match(watchRegex); 26 | 27 | if (onMatch && typeof ctx.attrs[key] === "function") { 28 | component.value.$on( 29 | `${onMatch[1][0].toLowerCase()}${onMatch[1].slice(1)}`, 30 | props[key] 31 | ); 32 | } 33 | 34 | if (watchMatch && typeof props[key] === "function") { 35 | watchers.push([ 36 | `${watchMatch[1][0].toLowerCase()}${watchMatch[1].slice(1)}`, 37 | props[key], 38 | ]); 39 | } 40 | } 41 | 42 | if (watchers.length) { 43 | if (component.value.$$.update) { 44 | const update = component.value.$$.update; 45 | component.value.$$.update = function () { 46 | watchers.forEach(([name, callback]) => { 47 | const index = component.value.$$.props[name]; 48 | const prop = component.value.$$.ctx[index]; 49 | prop && callback(prop); 50 | }); 51 | update.apply(null, arguments); 52 | }; 53 | } 54 | } 55 | 56 | return () => component.value.$destroy(); 57 | }); 58 | 59 | onUpdated(() => component.value?.$set(ctx.attrs)); 60 | 61 | onDeactivated(() => component.value?.$destroy()); 62 | 63 | return { 64 | component, 65 | container, 66 | }; 67 | }, 68 | render() { 69 | const children = { 70 | ref: "container", 71 | id: wrapperProps.id, 72 | style: {...wrapperProps.styles}, 73 | class: wrapperProps.className, 74 | }; 75 | return h(wrapperProps.element, children); 76 | }, 77 | }); 78 | }; 79 | -------------------------------------------------------------------------------- /packages/cli/src/helpers/utils.ts: -------------------------------------------------------------------------------- 1 | import path from "path"; 2 | import * as fs from "fs-extra"; 3 | import camelCase from "camelcase"; 4 | import { ModuleFormat, NormalizedOpts, WatchOpts } from "../types"; 5 | import { concatAllArray } from "jpjs"; 6 | import glob from "tiny-glob/sync"; 7 | import { paths } from "../constants"; 8 | import { PackageJson } from "../types"; 9 | import { logger } from "./logger"; 10 | 11 | let appPackageJson: PackageJson; 12 | 13 | try { 14 | appPackageJson = fs.readJSONSync(paths.appPackageJson); 15 | } catch (e) { 16 | logger.error(e); 17 | } 18 | 19 | // Remove the package name scope if it exists 20 | export const removeScope = (name: string) => name.replace(/^@.*\//, ""); 21 | 22 | // UMD-safe package name 23 | export const safeVariableName = (name: string) => 24 | camelCase( 25 | removeScope(name) 26 | .toLowerCase() 27 | .replace(/((^[^a-zA-Z]+)|[^\w.-])|([^a-zA-Z0-9]+$)/g, "") 28 | ); 29 | 30 | export const safePackageName = (name: string) => 31 | name 32 | .toLowerCase() 33 | .replace(/(^@.*\/)|((^[^a-zA-Z]+)|[^\w.-])|([^a-zA-Z0-9]+$)/g, ""); 34 | 35 | export const external = (id: string) => 36 | !id.startsWith(".") && !path.isAbsolute(id); 37 | 38 | export async function cleanDistFolder() { 39 | await fs.remove(paths.appDist); 40 | } 41 | 42 | // Make sure any symlinks in the project folder are resolved: 43 | // https://github.com/facebookincubator/create-react-app/issues/637 44 | export const appDirectory = fs.realpathSync(process.cwd()); 45 | export const resolvePath = function(relativePath: string) { 46 | return path.resolve(appDirectory, relativePath); 47 | }; 48 | 49 | export const isDir = (name: string) => 50 | fs 51 | .stat(name) 52 | .then(stats => stats.isDirectory()) 53 | .catch(() => false); 54 | 55 | export const isFile = (name: string) => 56 | fs 57 | .stat(name) 58 | .then(stats => stats.isFile()) 59 | .catch(() => false); 60 | 61 | async function getInputs( 62 | entries?: string | string[], 63 | source?: string 64 | ): Promise { 65 | return concatAllArray( 66 | ([] as any[]) 67 | .concat( 68 | entries && entries.length 69 | ? entries 70 | : (source && resolvePath(source)) || (await isDir(resolvePath("src"))) 71 | ) 72 | .map(file => glob(file)) 73 | ); 74 | } 75 | 76 | export async function normalizeOpts(opts: WatchOpts): Promise { 77 | if (opts.commandName === "start") { 78 | return { 79 | ...opts, 80 | name: "bundle", 81 | input: await getInputs(opts.entry), 82 | format: ["iife"] as [ModuleFormat, ...ModuleFormat[]] 83 | }; 84 | } else { 85 | return { 86 | ...opts, 87 | name: opts.fileName || appPackageJson.name, 88 | input: await getInputs(opts.entry, appPackageJson.source), 89 | format: 90 | opts.format && 91 | (opts.format.split(",").map((format: string) => { 92 | if (format === "es") { 93 | return "esm"; 94 | } 95 | return format; 96 | }) as [ModuleFormat, ...ModuleFormat[]]) 97 | }; 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

svere

2 | 3 |

4 | Write components once, run everywhere. Make svelte components run inside React or Vue applications. 5 |

6 | 7 |

8 | PRs Welcome 9 | License 10 | Downloads 11 | 12 |

13 | 14 | --- 15 | 16 | Managing support for libraries that provide UI components across frameworks is a pain, 17 | especially when [Web Component](https://developer.mozilla.org/en-US/docs/Web/Web_Components) are not an option (e.g. for server side rendering, best performance, etc). 18 | 19 | At present, the [svelte](https://svelte.dev/) framework is developing rapidly. 20 | It is a good backward compatibility solution to make svelte components run in the old `react` or `vue` project, 21 | especially when the team's technology stack is not unified, this provides an idea of cross-framework sharing component. 22 | 23 | 24 | # [CORE](https://github.com/FE-PIRL/svere/blob/master/packages/core/README.md) 25 | 26 | > #### Adapters for react、vue2 and vue3 27 | 28 | # [CLI](https://github.com/FE-PIRL/svere/blob/master/packages/cli/README.md) 29 | 30 | > #### An all-in-one cli for quickly create svelte components 31 | 32 | 33 | ## How does it work 34 | 35 |

36 | 37 |

38 | 39 | `Svere` contains several `adapters` for `React/Vue2/Vue3` which allows you to pass props and respond to events in a way that makes sense for that library. 40 | Also, it provides a [cli](https://github.com/FE-PIRL/svere/blob/master/packages/cli/README.md) to quickly create svelte components that can be shared across components. 41 | 42 |

43 | 44 |

45 | 46 | Svere use the life cycle hooks of each framework to complete the mounting, updating and uninstalling of svelte components. 47 | 48 | ## Value and efficacy 49 | 50 | 1. Increased efficiency 51 | 52 | > Focus on the writing of business components without caring about the user, which greatly improves the development efficiency 53 | 54 | 2. Cross-stack reuse 55 | 56 | > Solve the problem of sharing a single component between different technology stacks, and achieve a certain sense of environment independence 57 | 58 | 3. Visual Unity 59 | 60 | > Only one piece of code is maintained, and the performance of components under different technology stacks can be guaranteed to be consistent -------------------------------------------------------------------------------- /packages/core/src/react/__tests__/React.test.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom"; 3 | 4 | import toReact from "../index"; 5 | import SvelteComponent from "../../../../../examples/dist"; 6 | import { act } from "react-dom/test-utils"; 7 | 8 | describe("React", () => { 9 | let container: any; 10 | const name = "ben"; 11 | 12 | beforeEach(() => { 13 | container = document.createElement("div"); 14 | document.body.appendChild(container); 15 | }); 16 | 17 | afterEach(() => { 18 | document.body.removeChild(container); 19 | container = null; 20 | }); 21 | 22 | it("should render", async () => { 23 | const ReactComponent = toReact(SvelteComponent); 24 | 25 | // render the component 26 | act(() => { 27 | ReactDOM.render(, container); 28 | }); 29 | 30 | expect(container).toMatchSnapshot(); 31 | }); 32 | 33 | it("count increments when button is clicked", async () => { 34 | const ReactComponent = toReact(SvelteComponent); 35 | 36 | // render the component 37 | act(() => { 38 | ReactDOM.render(, container); 39 | }); 40 | 41 | const button = container.querySelector("button"); 42 | expect(button.textContent).toBe("add count: 0"); 43 | 44 | // Test second render and componentDidUpdate 45 | await act(async () => { 46 | button.dispatchEvent(new MouseEvent("click", { bubbles: true })); 47 | }); 48 | 49 | expect(button.textContent).toBe("add count: 1"); 50 | }); 51 | 52 | it("watch internal props changes and execute the callback", async () => { 53 | const ReactComponent = toReact(SvelteComponent); 54 | 55 | const handleSomeEvent = (name: string) => { 56 | //console.log(name) 57 | expect(name).toBe("yasin"); 58 | }; 59 | 60 | // render the component 61 | await act(async () => { 62 | ReactDOM.render( 63 | , 64 | container 65 | ); 66 | }); 67 | 68 | const button = container.querySelector("button"); 69 | 70 | // Test second render and componentDidUpdate 71 | await act(async () => { 72 | button.dispatchEvent(new MouseEvent("click", { bubbles: true })); 73 | }); 74 | }); 75 | 76 | it("listen custom event", async () => { 77 | const ReactComponent = toReact(SvelteComponent); 78 | 79 | const handleSomeEvent = (e: any) => { 80 | //console.log(e.detail) 81 | expect(e.detail).toBe(1); 82 | }; 83 | 84 | // render the component 85 | await act(async () => { 86 | ReactDOM.render( 87 | , 88 | container 89 | ); 90 | }); 91 | 92 | const button = container.querySelector("button"); 93 | 94 | // Test second render and componentDidUpdate 95 | await act(async () => { 96 | button.dispatchEvent(new MouseEvent("click", { bubbles: true })); 97 | }); 98 | }); 99 | 100 | it("custom wrapper", async () => { 101 | const wrapperProps = { 102 | element: "section", 103 | className: "section-css", 104 | id: "svelte-react", 105 | styles: { 106 | border: "1px solid gray", 107 | }, 108 | }; 109 | const ReactComponent = toReact(SvelteComponent, wrapperProps); 110 | 111 | // render the component 112 | act(() => { 113 | ReactDOM.render(, container); 114 | }); 115 | 116 | expect(container).toMatchSnapshot(); 117 | }); 118 | }); 119 | -------------------------------------------------------------------------------- /packages/cli/src/commands/lint.ts: -------------------------------------------------------------------------------- 1 | import execa from "execa"; 2 | import { logger } from "../helpers/logger"; 3 | import ora from "ora"; 4 | import { appName } from "../constants"; 5 | import path from "path"; 6 | 7 | export async function command(commandOptions: any) { 8 | try { 9 | if (commandOptions.debug) { 10 | logger.level = "debug"; 11 | } 12 | 13 | if (commandOptions.js) { 14 | const runSpinner = ora({ 15 | text: `Running eslint with prettier ...\n`, 16 | prefixText: `[${appName}]` 17 | }); 18 | try { 19 | runSpinner.start(); 20 | const subprocess = execa( 21 | "npx", 22 | ["eslint", "--fix", commandOptions.jsFiles], 23 | { 24 | cwd: "." 25 | } 26 | ); 27 | subprocess.stdout.pipe(process.stdout); 28 | subprocess.stderr.pipe(process.stderr); 29 | await (async () => { 30 | await subprocess; 31 | runSpinner.succeed(`End run eslint with prettier`); 32 | })(); 33 | } catch (e) { 34 | runSpinner.fail(`Failed run eslint with prettier :` + e); 35 | } 36 | } 37 | 38 | if (commandOptions.css) { 39 | const runSpinner = ora({ 40 | text: `Running stylelint with prettier ...\n`, 41 | prefixText: `[${appName}]` 42 | }); 43 | try { 44 | runSpinner.start(); 45 | await execa("npx", ["stylelint", "--fix", commandOptions.cssFiles], { 46 | cwd: "." 47 | }).stdout.pipe(process.stdout); 48 | runSpinner.succeed(`End run stylelint with prettier`); 49 | } catch (e) { 50 | runSpinner.fail(`Failed run stylelint with prettier :` + e); 51 | } 52 | } 53 | 54 | if (commandOptions.format) { 55 | const runSpinner = ora({ 56 | text: `Running prettier ...\n`, 57 | prefixText: `[${appName}]` 58 | }); 59 | try { 60 | runSpinner.start(); 61 | await execa( 62 | "npx", 63 | ["prettier", "--write", commandOptions.formatFiles], 64 | { 65 | cwd: "." 66 | } 67 | ).stdout.pipe(process.stdout); 68 | runSpinner.succeed(`End run prettier`); 69 | } catch (e) { 70 | runSpinner.fail(`Failed run prettier :` + e); 71 | } 72 | } 73 | 74 | if (!commandOptions.js && !commandOptions.css && !commandOptions.format) { 75 | const runSpinner = ora({ 76 | text: `Running eslint and stylelint with prettier ...\n`, 77 | prefixText: `[${appName}]` 78 | }); 79 | try { 80 | runSpinner.start(); 81 | const subprocess = execa( 82 | "npx", 83 | ["eslint", "--fix", commandOptions.jsFiles], 84 | { 85 | cwd: "." 86 | } 87 | ); 88 | subprocess.stdout.pipe(process.stdout); 89 | subprocess.stderr.pipe(process.stderr); 90 | 91 | await (async () => { 92 | await subprocess; 93 | await execa("npx", ["stylelint", commandOptions.cssFiles], { 94 | cwd: "." 95 | }).stdout.pipe(process.stdout); 96 | await execa( 97 | "npx", 98 | ["prettier", "--write", commandOptions.formatFiles], 99 | { 100 | cwd: "." 101 | } 102 | ).stdout.pipe(process.stdout); 103 | runSpinner.succeed(`End run eslint and stylelint with prettier`); 104 | })(); 105 | } catch (e) { 106 | runSpinner.fail(`Failed run eslint and stylelint with prettier :` + e); 107 | } 108 | } 109 | } catch (error) { 110 | logger.error(error); 111 | process.exit(1); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /packages/cli/src/helpers/logger.ts: -------------------------------------------------------------------------------- 1 | import * as colors from "kleur/colors"; 2 | import { LoggerLevel, LoggerEvent, LoggerOptions } from "../types"; 3 | import { appName } from "../constants"; 4 | 5 | export interface LogRecord { 6 | val: string; 7 | count: number; 8 | } 9 | 10 | const levels: Record = { 11 | debug: 20, 12 | info: 30, 13 | warn: 40, 14 | error: 50, 15 | silent: 90 16 | }; 17 | 18 | class Logger { 19 | /** set the log level (can be changed after init) */ 20 | public level: LoggerLevel = "info"; 21 | /** configure maximum number of logs to keep (default: 500) */ 22 | public logCount = 500; 23 | 24 | private history: { val: string; count: number }[] = []; // this is immutable; must be accessed with Logger.getHistory() 25 | private callbacks: Record void> = { 26 | debug: (message: string) => { 27 | console.log(message); 28 | }, 29 | info: (message: string) => { 30 | console.log(message); 31 | }, 32 | warn: (message: string) => { 33 | console.warn(message); 34 | }, 35 | error: (message: string) => { 36 | console.error(message); 37 | } 38 | }; 39 | 40 | private log({ 41 | level, 42 | name, 43 | message, 44 | task 45 | }: { 46 | level: LoggerEvent; 47 | name: string; 48 | message: string; 49 | task?: Function; 50 | }) { 51 | // test if this level is enabled or not 52 | if (levels[this.level] > levels[level]) { 53 | return; // do nothing 54 | } 55 | 56 | // format 57 | let text = message; 58 | if (level === "warn") text = colors.yellow(text); 59 | if (level === "error") text = colors.red(text); 60 | const log = `${colors.dim(`[${name}]`)} ${text}`; 61 | 62 | // add to log history and remove old logs to keep memory low 63 | const lastHistoryItem = this.history[this.history.length - 1]; 64 | if (lastHistoryItem && lastHistoryItem.val === log) { 65 | lastHistoryItem.count++; 66 | } else { 67 | this.history.push({ val: log, count: 1 }); 68 | } 69 | while (this.history.length > this.logCount) { 70 | this.history.shift(); 71 | } 72 | 73 | // log 74 | if (typeof this.callbacks[level] === "function") { 75 | this.callbacks[level](log); 76 | } else { 77 | throw new Error(`No logging method defined for ${level}`); 78 | } 79 | 80 | // logger takes a possibly processor-intensive task, and only 81 | // processes it when this log level is enabled 82 | task && task(this); 83 | } 84 | 85 | /** emit messages only visible when --debug is passed */ 86 | public debug(message: string, options?: LoggerOptions): void { 87 | const name = (options && options.name) || appName; 88 | this.log({ level: "debug", name, message, task: options?.task }); 89 | } 90 | 91 | /** emit general info */ 92 | public info(message: string, options?: LoggerOptions): void { 93 | const name = (options && options.name) || appName; 94 | this.log({ level: "info", name, message, task: options?.task }); 95 | } 96 | 97 | /** emit non-fatal warnings */ 98 | public warn(message: string, options?: LoggerOptions): void { 99 | const name = (options && options.name) || appName; 100 | this.log({ level: "warn", name, message, task: options?.task }); 101 | } 102 | 103 | /** emit critical error messages */ 104 | public error(message: string, options?: LoggerOptions): void { 105 | const name = (options && options.name) || appName; 106 | this.log({ level: "error", name, message, task: options?.task }); 107 | } 108 | 109 | /** get full logging history */ 110 | public getHistory(): ReadonlyArray { 111 | return this.history; 112 | } 113 | 114 | /** listen for events */ 115 | public on(event: LoggerEvent, callback: (message: string) => void) { 116 | this.callbacks[event] = callback; 117 | } 118 | } 119 | 120 | /** export one logger to rest of app */ 121 | export const logger = new Logger(); 122 | -------------------------------------------------------------------------------- /packages/cli/src/cli.ts: -------------------------------------------------------------------------------- 1 | import program from "commander"; 2 | import * as fs from "fs-extra"; 3 | import { paths } from "./constants"; 4 | import { command as initCommand } from "./commands/create"; 5 | import { command as startCommand } from "./commands/start"; 6 | import { command as buildCommand } from "./commands/build"; 7 | import { command as testCommand } from "./commands/test"; 8 | import { command as lintCommand } from "./commands/lint"; 9 | import { command as docCommand } from "./commands/doc"; 10 | const { version } = fs.readJSONSync(paths.appPackageJson); 11 | 12 | export async function main() { 13 | program 14 | .version(version, "-v, --version") 15 | .description("svere - develop svelte apps with svere"); 16 | 17 | program 18 | .command("create [targetDir]") 19 | .description( 20 | 'create a new project. If you do not specify targetDir, "svere-default" will be used' 21 | ) 22 | .option( 23 | "-t, --template ", 24 | `specify template for new project`, 25 | "default" 26 | ) 27 | .option( 28 | "-pm, --packageManager ", 29 | 'which package manager to use. ["npm","pnpm","yarn","yarn2"]', 30 | "npm" 31 | ) 32 | .option( 33 | "-f, --force", 34 | "force operation even if targetDir exists and is not empty", 35 | false 36 | ) 37 | .option("-c, --cache", "cache template for later use", false) 38 | .option("-d, --debug", "more debug logging", false) 39 | .option("-si, --skip-install", "skip install", false) 40 | .option("-sg, --skip-git", "skip git init", false) 41 | .option("-sc, --skip-commit", "skip initial commit", false) 42 | .action(async (targetDir, cmd) => { 43 | const options = cmd.opts(); 44 | options.targetDir = targetDir; 45 | await initCommand(options); 46 | }); 47 | 48 | program 49 | .command("start") 50 | .description("start development server") 51 | .option("--entry ", "specify entry file for dev", "src/main.ts") 52 | .option("-d, --debug", "more debug logging", false) 53 | .option("-s, --silent", "never launch the browser", false) 54 | .option("-p, --port ", "specify port for dev", "5000") 55 | .action(async cmd => { 56 | const options = cmd.opts(); 57 | options.commandName = cmd._name; 58 | await startCommand(options); 59 | }); 60 | 61 | program 62 | .command("build") 63 | .description("Build your component once and exit") 64 | .option( 65 | "--entry ", 66 | "specify entry file for build", 67 | "src/components/index.ts" 68 | ) 69 | .option("--fileName ", "specify fileName exposed in UMD builds") 70 | .option("--format ", "specify module format(s)", "umd,esm") 71 | .option("--transpileOnly", "skip type checking", true) 72 | .option("-d, --debug", "more debug logging", false) 73 | .option("-bsb, --buildStorybook", "build storybook to static files", false) 74 | .action(async cmd => { 75 | const options = cmd.opts(); 76 | options.commandName = cmd._name; 77 | await buildCommand(options); 78 | }); 79 | 80 | program 81 | .command("test") 82 | .description("Run cypress and jest test runner") 83 | .option("-co, --cypressOpen", "run cypress open", false) 84 | .option("-p, --port ", "specify port for test", "5000") 85 | .action(async cmd => { 86 | const options = cmd.opts(); 87 | await testCommand(options); 88 | }); 89 | 90 | program 91 | .command("lint") 92 | .description("Run eslint and stylelint with prettier") 93 | .option("-js, --js", "run eslint with prettier only", false) 94 | .option("-css, --css", "run stylelint with prettier only", false) 95 | .option("-f, --format", "run prettier only", false) 96 | .option( 97 | "-jfs, --jsFiles ", 98 | "specify files for eslint", 99 | "src/**/*.{js,jsx,ts,tsx,svelte}" 100 | ) 101 | .option( 102 | "-cfs, --cssFiles ", 103 | "specify files for stylelint", 104 | "src/**/*.{less,postcss,css,scss,svelte}" 105 | ) 106 | .option( 107 | "-ffs, --formatFiles ", 108 | "specify files for prettier", 109 | "src/**/*.{js,json,ts,tsx,svelte,css,less,scss,html,md}" 110 | ) 111 | .action(async cmd => { 112 | const options = cmd.opts(); 113 | await lintCommand(options); 114 | }); 115 | 116 | program 117 | .command("doc") 118 | .description("Start storybook for component") 119 | .option("-p, --port ", "specify port to run storybook", "6006") 120 | .option("-b, --build", "build storybook to static files", false) 121 | .action(async cmd => { 122 | const options = cmd.opts(); 123 | await docCommand(options); 124 | }); 125 | 126 | await program.parseAsync(process.argv); 127 | } 128 | -------------------------------------------------------------------------------- /packages/core/README_ZH.md: -------------------------------------------------------------------------------- 1 | # SVERE CORE 2 | 3 | svere的核心适配器. 4 | 5 |

6 | PRs Welcome 7 | License 8 | Downloads 9 | 10 |

11 | 12 | 简体中文 | [English](https://github.com/FE-PIRL/svere/blob/master/packages/core/README.md) 13 | 14 | --- 15 | 16 | - [介绍](#介绍) 17 | - [安装](#安装) 18 | - [用法](#用法) 19 | - [示例](#示例) 20 | - [React](#react) 21 | - [Vue2](#vue2) 22 | - [Vue3](#vue3) 23 | - [命令行](#命令行) 24 | - [待办](#待办) 25 | 26 | # 介绍 27 | 28 | 众所周知, 使提供UI组件的库跨框架使用是一件痛苦的事, 29 | 尤其当[Web Component](https://developer.mozilla.org/en-US/docs/Web/Web_Components) 不是一个选项的时候(比如服务端渲染,最佳性能等). 30 | 31 | 目前, [svelte](https://www.sveltejs.cn/) 框架正在快速发展. 32 | 让svelte组件跑在旧的`react`或`vue` 工程中是一个好的向后兼容的方案, 33 | 尤其当团队的技术栈不统一时,这提供了一个跨框架共享组件的思路. 34 | 35 | `Svere`包含了几个对`React/Vue2/Vue3`的`适配器`, 允许你使用一种对组件有意义的方式传递参数和响应事件. 36 | 同时,它提供一个[cli](https://github.com/FE-PIRL/svere/blob/master/packages/cli/README_ZH.md) 可以用来快速创建可跨框架共享的组件的模板. 37 | 38 | # 安装 39 | 40 | 使用 [npm](https://www.npmjs.com/): 41 | 42 | ```bash 43 | npm install @svere/core 44 | ``` 45 | 46 | 或 [yarn](https://yarnpkg.com/lang/en/): 47 | 48 | ```bash 49 | yarn add @svere/core 50 | ``` 51 | 52 | # 用法 53 | 54 | svere的核心暴露了几个适配器函数, 可称之为`toReact`, `toVue` 和 `toVue3`. 55 | 每个适配器都是一个简单的函数, 它接受一个svelte组件和一些选项并且返回可以在Vue模板或JSX中使用的Vue或React组件. 56 | 57 | 所有的适配器函数都有如下相同的入参, 例如: 58 | ```ts 59 | toReact(Component: SvelteComponent, wrapperProps?: WrapperProps) : Component 60 | ``` 61 | - `Component` 是一个编译过的svelte组件,使用rollup时配合rollup-plugin-svelte,或使用webpack时配合svelte-loader,作为构建过程中的一步进行预编译或编译. 62 | - `wrapperProps` (可选) 是一个包裹对象, 包含了 `element`, `id`, `className` 和 `styles`. 63 | - `element` : 所有组件都有一个基础的包裹元素, 默认是`
` 但你也可以传入一个字符串来定制它 (比如: 'span', 'li' 等.) 64 | - `id` : 在基础包裹元素上添加id属性, 默认是`svelte-wrapper`. 65 | - `className` : 在基础包裹元素上添加class属性,允许你在样式文件定义实现. 66 | - `styles` : 在基础包裹元素上添加内联样式属性, 可以覆盖`className`中的样式定义. 67 | 68 | # 示例 69 | 70 | 在下面的例子中, 我们要使用的[svelte component](https://github.com/FE-PIRL/svere/blob/master/examples/src/Component.svelte) 是一个接受prop后渲染, 通过点击触发事件的简单组件. 71 | 将它打包成`umd`格式的单文件, 以便于可以被其它框架方便地引入. 72 | 73 | ```sveltehtml 74 | 89 | 90 |
91 |

Hello {name}, welcome!

92 | 95 | 98 |
99 | 100 | 115 | ``` 116 | 117 | ## React 118 | 119 | ```jsx 120 | import React, { useState } from "react"; 121 | import SvelteComponent from "./svelte-component"; 122 | import toReact from '@svere/core/dist/react.js' 123 | 124 | const wrapperProps = { 125 | element: "section", 126 | className: "section-css", 127 | id: "svelte-react", 128 | styles: { 129 | border: "1px solid gray", 130 | }, 131 | }; 132 | const ReactComponent = toReact(SvelteComponent, wrapperProps); 133 | 134 | const App = () => { 135 | const [name, setName] = useState('ben'); 136 | const changeName = () => setName(n => { 137 | return name === 'ben' ? 'yasin' : 'ben' 138 | }); 139 | 140 | const handleEventCallback = (e) => { 141 | console.log(e.detail) 142 | }; 143 | 144 | const handleWatchCallback = (name) => { 145 | console.log(name) 146 | }; 147 | 148 | return ( 149 |
150 | 155 | 156 |
157 | 158 | 159 |
160 | ); 161 | }; 162 | ``` 163 | 164 | ## Vue2 165 | 166 | ```vue 167 | 177 | 178 | 204 | ``` 205 | 206 | ## Vue3 207 | 208 | ```vue 209 | 218 | 219 | 256 | 257 | 262 | ``` 263 | 264 | # 命令行 265 | 使用我们的[CLI](https://github.com/FE-PIRL/svere/tree/master/packages/cli) 在本地尝试svere. 266 | 267 | 使用 [npm](https://www.npmjs.com/): 268 | 269 | ```bash 270 | npm install @svere/core 271 | ``` 272 | 273 | 或 [yarn](https://yarnpkg.com/lang/en/): 274 | 275 | ```bash 276 | yarn add @svere/core 277 | ``` 278 | 279 | # 待办 280 | 281 | - 添加更多特性到core中, 例如: 子组件、插槽. -------------------------------------------------------------------------------- /packages/cli/src/commands/create.ts: -------------------------------------------------------------------------------- 1 | import ora from "ora"; 2 | import path from "path"; 3 | import execa from "execa"; 4 | import fs from "fs-extra"; 5 | import degit from "degit"; 6 | import * as colors from "kleur/colors"; 7 | import { logger } from "../helpers/logger"; 8 | import { appName } from "../constants"; 9 | import { LoggerOptions, PackageJson } from "../types"; 10 | const pkg: PackageJson = require(path.join(__dirname, "../../package.json")); 11 | //TODO fetch from remote github 12 | const templates = ["default"]; 13 | 14 | export async function command(commandOptions: any) { 15 | const force = commandOptions.force; 16 | const cache = commandOptions.cache; 17 | const debug = commandOptions.debug; 18 | const template = commandOptions.template; 19 | if (debug) { 20 | logger.level = "debug"; 21 | } 22 | 23 | const targetDir = path.join( 24 | process.cwd(), 25 | commandOptions.targetDir || `svere-${template.replace("/", "-")}` 26 | ); 27 | if (!templates.includes(template)) { 28 | logger.error( 29 | `invalid template ${template}. Valid: ${colors.green( 30 | JSON.stringify(templates) 31 | )}` 32 | ); 33 | return; 34 | } 35 | 36 | await installTemplate(targetDir, template, force, cache, debug); 37 | await updatePackage(targetDir); 38 | 39 | if (!commandOptions.skipGit) { 40 | await gitInit(targetDir); 41 | if (!commandOptions.skipCommit) { 42 | await gitCommit(targetDir); 43 | } 44 | } 45 | 46 | if (!commandOptions.skipInstall) { 47 | await installDependencies(targetDir, commandOptions.packageManager); 48 | } 49 | 50 | await quickStart( 51 | targetDir, 52 | commandOptions.packageManager, 53 | !commandOptions.skipInstall 54 | ); 55 | } 56 | 57 | async function installTemplate(targetDir, template, force, cache, debug) { 58 | const cloneSpinner = ora({ 59 | text: `Creating ${colors.cyan(targetDir)} from template ...\n`, 60 | prefixText: `[${appName}]` 61 | }); 62 | try { 63 | cloneSpinner.start(); 64 | const empty = 65 | !fs.existsSync(targetDir) || !fs.readdirSync(targetDir).length; 66 | if (!empty) { 67 | if (!force) { 68 | throw Error( 69 | `Directory ${colors.cyan( 70 | targetDir 71 | )} not empty, use -f or --force to overwrite it` 72 | ); 73 | } 74 | } 75 | await fs.emptyDir(targetDir); 76 | 77 | const githubRepo = pkg.repository.templates.match( 78 | /github\.com\/(.*).git/ 79 | )[1]; 80 | const beta = pkg.version.indexOf("beta") > -1; 81 | const degitPath = `${githubRepo}/${template}${beta ? "#beta" : ""}`; 82 | const degitOptions = { 83 | cache, 84 | force, 85 | verbose: debug, 86 | mode: "tar" 87 | }; 88 | if (debug) { 89 | logger.debug( 90 | `degit ${colors.cyan(degitPath)}`, 91 | degitOptions as LoggerOptions 92 | ); 93 | } 94 | const emitter = degit(degitPath, degitOptions); 95 | 96 | emitter.on("info", info => { 97 | logger.info(info.message); 98 | }); 99 | emitter.on("warn", warning => { 100 | logger.warn(warning.message); 101 | }); 102 | emitter.on("error", error => { 103 | logger.error(error.message, error); 104 | throw Error(error.message); 105 | }); 106 | 107 | await emitter.clone(targetDir); 108 | cloneSpinner.succeed(`Created ${colors.cyan(targetDir)} successfully`); 109 | } catch (error) { 110 | cloneSpinner.fail(`Failed to create ${colors.cyan(targetDir)}`); 111 | logger.error(error.message); 112 | process.exit(1); 113 | } 114 | } 115 | 116 | async function updatePackage(dir) { 117 | const pkgFile = path.join(dir, "package.json"); 118 | const pkg = require(pkgFile); 119 | pkg.name = path.basename(dir); 120 | fs.writeFileSync(pkgFile, JSON.stringify(pkg, null, 2)); 121 | } 122 | 123 | function _installProcess(packageManager, npmInstallOptions) { 124 | switch (packageManager) { 125 | case "npm": 126 | return execa( 127 | "npm", 128 | ["install", "--loglevel", "error"], 129 | npmInstallOptions 130 | ); 131 | case "yarn": 132 | return execa("yarn", ["--silent"], npmInstallOptions); 133 | case "pnpm": 134 | return execa("pnpm", ["install", "--reporter=silent"], npmInstallOptions); 135 | default: 136 | throw new Error("Unspecified package installer."); 137 | } 138 | } 139 | 140 | async function installDependencies(dir, packageManager) { 141 | const installSpinner = ora({ 142 | text: `Installing dependencies, this might take a while ...\n`, 143 | prefixText: `[${appName}]` 144 | }); 145 | try { 146 | installSpinner.start(); 147 | const npmInstallOptions = { 148 | cwd: dir, 149 | stdio: "inherit" 150 | } as any; 151 | 152 | const npmInstallProcess = _installProcess( 153 | packageManager, 154 | npmInstallOptions 155 | ); 156 | npmInstallProcess.stdout && npmInstallProcess.stdout.pipe(process.stdout); 157 | npmInstallProcess.stderr && npmInstallProcess.stderr.pipe(process.stderr); 158 | await npmInstallProcess; 159 | 160 | installSpinner.succeed("Installed dependencies"); 161 | } catch (error) { 162 | installSpinner.fail("Failed to install dependencies"); 163 | logger.error(error.message); 164 | process.exit(1); 165 | } 166 | } 167 | 168 | async function gitInit(dir) { 169 | const gitInitSpinner = ora({ 170 | text: `Initialing git repo ...\n`, 171 | prefixText: `[${appName}]` 172 | }); 173 | try { 174 | gitInitSpinner.start(); 175 | await execa("git", ["init"], { cwd: dir }); 176 | gitInitSpinner.succeed("Initialized git repo"); 177 | } catch (error) { 178 | gitInitSpinner.fail("Failed to initialize git repo"); 179 | logger.error(`Failed to initialize git in ${dir}` + error.message); 180 | process.exit(1); 181 | } 182 | } 183 | 184 | async function gitCommit(dir) { 185 | const gitCommitSpinner = ora({ 186 | text: `Committing git ...\n`, 187 | prefixText: `[${appName}]` 188 | }); 189 | try { 190 | gitCommitSpinner.start(); 191 | await execa("git", ["add", "."], { cwd: dir }); 192 | await execa("git", ["commit", "-m initial commit"], { cwd: dir }); 193 | gitCommitSpinner.succeed("Completed initial commit"); 194 | } catch (error) { 195 | gitCommitSpinner.fail("Failed to commit git"); 196 | logger.error(`Failed to commit git in ${dir}` + error.message); 197 | process.exit(1); 198 | } 199 | } 200 | 201 | async function quickStart(dir, pm, installed) { 202 | function _formatCommand(command, description) { 203 | return " " + command.padEnd(17) + colors.dim(description); 204 | } 205 | 206 | console.log(``); 207 | console.log(colors.bold(colors.underline(`Quickstart:`))); 208 | console.log(``); 209 | console.log(` cd ${dir}`); 210 | console.log(` ${pm} start`); 211 | console.log(``); 212 | console.log(colors.bold(colors.underline(`All Commands:`))); 213 | console.log(``); 214 | console.log( 215 | _formatCommand( 216 | `${pm} install`, 217 | `Install your dependencies. ${ 218 | installed 219 | ? "(We already ran this one for you!)" 220 | : "(You asked us to skip this step!)" 221 | }` 222 | ) 223 | ); 224 | console.log(_formatCommand(`${pm} start`, "Start development server.")); 225 | console.log(_formatCommand(`${pm} run build`, "Build your component.")); 226 | console.log( 227 | _formatCommand(`${pm} run lint`, "Check code quality and formatting.") 228 | ); 229 | console.log(_formatCommand(`${pm} run test`, "Run your tests.")); 230 | console.log(_formatCommand(`${pm} run doc`, "Run storybook for component.")); 231 | console.log(``); 232 | } 233 | -------------------------------------------------------------------------------- /packages/cli/README_ZH.md: -------------------------------------------------------------------------------- 1 | # SVERE CLI 2 | 3 | 为svere配套的脚手架. 4 | 5 |

6 | PRs Welcome 7 | License 8 | Downloads 9 | 10 |

11 | 12 | [English](https://github.com/FE-PIRL/svere/blob/master/packages/cli/README.md) | 简体中文 13 | 14 | --- 15 | 16 | - [介绍](#intro) 17 | - [特性](#features) 18 | - [安装](#install) 19 | - [快速开始](#quick-start) 20 | - [定制化](#customization) 21 | - [发布](#publishing) 22 | - [API参考](#api-reference) 23 | - [作者](#author) 24 | - [协议](#license) 25 | 26 | # 介绍 27 | 28 | 尽管你可以自己搭建一个工程来生产满足要求的目标文件,但你仍然会有大量的工作要做。我们的目的是帮你处理掉这些繁琐的工作,让你专心于业务组件的开发。`Svere cli`是一个零配置的脚手架,涉及到组件的`开发`,`打包`,`语法检查`、`测试`、`文档编写`、`发布`的生命周期,使你有一个轻松的组件开发体验. 29 | 30 | # 特性 31 | 32 | * 实时Reload与Watch模式 33 | * 使用Typescript书写 34 | * 通过`svere test`执行crypress与Jest 35 | * 通过`svere lint`执行Eslint、Stylelint与Prettier 36 | * 通过`svere doc`驱动Storybook 37 | * 通过`svere.config.js`以及[code-specification-unid](https://github.com/FE-PIRL/code-specification-unid)的导出文件来定制化 38 | 39 | # 安装 40 | 41 | 使用 [npm](https://www.npmjs.com/): 42 | 43 | ```bash 44 | npm install -g @svere/cli 45 | ``` 46 | 47 | 或者使用 [yarn](https://yarnpkg.com/lang/en/): 48 | 49 | ```bash 50 | yarn add global @svere/cli 51 | ``` 52 | 53 | # 快速安装 54 | 55 | ```bash 56 | svere create mycom 57 | cd mycom 58 | yarn start 59 | ``` 60 | 61 | 就是这样,您不需要担心TypeScript、Rollup、Jest或其他工具的配置. 只要开始编辑`src/components/MyComponent.svelte`就可以了! 62 | 63 | 下面是您可能会发现有用的命令列表: 64 | 65 | ```npm start``` or ```yarn start``` 66 | 67 | 以开发/监视模式运行项目。您的项目将在更改后重新加载 68 | 69 | ```npm run build``` or ```yarn build``` 70 | 71 | 使用Rollup, 将包输出到dist文件夹, 经过优化后打成多种格式(UMD和ES模块). 72 | 73 | ```npm test``` or ```yarn test``` 74 | 75 | 本模板使用了 [Cypress](https://www.cypress.io/) 与 [testing-library](https://testing-library.com/docs/cypress-testing-library/intro/) 用来测试. 76 | 77 | 如果您打算测试您的组件,强烈建议您阅读他们的文档. 您可以通过运行`svere test-co`来见证一个简单的示例. 78 | 79 | ```npm run lint``` or ```yarn lint``` 80 | 81 | 本模板整合了 [code-specification-unid](https://github.com/FE-PIRL/code-specification-unid) 用来作质量检查. 82 | 83 | 如果您打算检查您的组件,强烈建议您阅读他们的文档. 84 | 85 | 默认的不带选项的命令将会驱动Eslint和Stylelint与Prettier一起工作. 86 | 87 | ```npm run doc``` or ```yarn doc``` 88 | 89 | 本模板整合了 [Storybook](https://storybook.js.org/) 用来作文档编写. 90 | 91 | 你可以使用命令`svere doc -b`来生成静态文档. 92 | 93 | 94 | # 定制化 95 | 96 | ### Rollup 97 | 98 | > **❗⚠️❗ 警告 **:
99 | > 这些修改将覆盖SVERE的默认行为和配置, 因为它们可以使内部保证和假设失效。这些类型的更改会破坏内部行为,并且对更新非常脆弱。谨慎使用! 100 | SVERE使用了Rollup作为打包工具. 大多数的默认配置都是固定的. 但是,如果您确实希望更改rollup的配置, 可以在项目的根目录下创建一个名为`svere.config.js`的文件,如下所示: 101 | 102 | ```js 103 | // Not transpiled with TypeScript or Babel, so use plain Es6/Node.js! 104 | module.exports = { 105 | // This function will run for each entry/format/env combination 106 | rollup(config, options) { 107 | return config; // always return a config. 108 | }, 109 | }; 110 | ``` 111 | 112 | 对象 `options` 包含了以下属性: 113 | 114 | ```tsx 115 | export interface SvereOptions { 116 | // Name of package 117 | name: string; 118 | // Port for dev 119 | port: number; 120 | // path to file 121 | input: string; 122 | // never launch browser automatically 123 | silent: boolean; 124 | // Module format 125 | format: 'umd' | 'esm' | 'iife'; 126 | // Environment 127 | env: 'development' | 'production'; 128 | // Path to tsconfig file 129 | tsconfig?: string; 130 | // Is error extraction running? 131 | extractErrors?: boolean; 132 | // Is minifying? 133 | minify?: boolean; 134 | // Is this the very first rollup config (and thus should one-off metadata be extracted)? 135 | writeMeta?: boolean; 136 | // Only transpile, do not type check (makes compilation faster) 137 | transpileOnly?: boolean; 138 | } 139 | ``` 140 | 141 | #### 示例: 添加Postcss插件 142 | 143 | ```js 144 | const postcss = require('rollup-plugin-postcss'); 145 | const autoprefixer = require('autoprefixer'); 146 | const cssnano = require('cssnano'); 147 | 148 | module.exports = { 149 | rollup(config, options) { 150 | config.plugins.push( 151 | postcss({ 152 | plugins: [ 153 | autoprefixer(), 154 | cssnano({ 155 | preset: 'default', 156 | }), 157 | ], 158 | inject: false, 159 | // only write out CSS for the first bundle (avoids pointless extra files): 160 | extract: !!options.writeMeta, 161 | }) 162 | ); 163 | return config; 164 | }, 165 | }; 166 | ``` 167 | 168 | 169 | # 发布 170 | 171 | 172 | # API参考 173 | 174 | ### ```svere start``` 175 | 176 | ```bash 177 | Usage: svere start [options] 178 | 179 | start development server 180 | 181 | Options: 182 | --entry specify entry file for dev (default: "src/main.ts") 183 | -d, --debug more debug logging (default: false) 184 | -s, --silent never launch the browser (default: false) 185 | -p, --port specify port for dev (default: "5000") 186 | -h, --help display help for command 187 | ``` 188 | 189 | 190 | ### ```svere build``` 191 | 192 | ```bash 193 | Usage: svere build [options] 194 | 195 | Build your component once and exit 196 | 197 | Options: 198 | --entry specify entry file for build (default: "src/components/index.ts") 199 | --fileName specify fileName exposed in UMD builds 200 | --format specify module format(s) (default: "umd,esm") 201 | --transpileOnly skip type checking (default: true) 202 | -d, --debug more debug logging (default: false) 203 | -bsb, --buildStorybook build storybook to static files (default: false) 204 | -h, --help display help for command 205 | ``` 206 | 207 | 208 | ### ```svere test``` 209 | 210 | ```bash 211 | Usage: svere test [options] 212 | 213 | Run cypress and jest test runner 214 | 215 | Options: 216 | -co, --cypressOpen run cypress open (default: false) 217 | -p, --port specify port for test (default: "5000") 218 | -h, --help display help for command 219 | ``` 220 | 221 | 222 | ### ```svere lint``` 223 | 224 | ```bash 225 | Usage: svere lint [options] 226 | 227 | Run eslint and stylelint with prettier 228 | 229 | Options: 230 | -js, --js run eslint with prettier only (default: false) 231 | -css, --css run stylelint with prettier only (default: false) 232 | -f, --format run prettier only (default: false) 233 | -jfs, --jsFiles specify files for eslint (default: "src/**/*.{js,jsx,ts,tsx,svelte}") 234 | -cfs, --cssFiles specify files for stylelint (default: "src/**/*.{less,postcss,css,scss,svelte}") 235 | -ffs, --formatFiles specify files for prettier (default: "src/**/*.{js,json,ts,tsx,svelte,css,less,scss,html,md}") 236 | -h, --help display help for command 237 | ``` 238 | 239 | 240 | ### ```svere doc``` 241 | 242 | ```bash 243 | Usage: svere doc [options] 244 | 245 | Start storybook for component 246 | 247 | Options: 248 | -p, --port specify port to run storybook (default: "6006") 249 | -b, --build build storybook to static files (default: false) 250 | -h, --help display help for command 251 | ``` 252 | 253 | # 作者 254 | 255 | [benyasin](https://github.com/benyasin) 256 | 257 | # 协议 258 | 259 | [MIT](https://oss.ninja/mit/jaredpalmer/) 260 | 261 | 262 | # 待办 263 | 264 | * 添加一个命令来发布组件到npm或cdn上 265 | 266 | -------------------------------------------------------------------------------- /packages/cli/src/helpers/createRollupConfig.ts: -------------------------------------------------------------------------------- 1 | import { safeVariableName, safePackageName, external } from "./utils"; 2 | import { paths } from "../constants"; 3 | import { RollupOptions } from "rollup"; 4 | import { terser } from "rollup-plugin-terser"; 5 | import commonjs from "@rollup/plugin-commonjs"; 6 | import json from "@rollup/plugin-json"; 7 | import replace from "@rollup/plugin-replace"; 8 | import resolve, { 9 | DEFAULTS as RESOLVE_DEFAULTS 10 | } from "@rollup/plugin-node-resolve"; 11 | import sourceMaps from "rollup-plugin-sourcemaps"; 12 | import typescript from "rollup-plugin-typescript2"; 13 | import ts from "typescript"; 14 | import { SvereOptions } from "../types"; 15 | import svelte from "rollup-plugin-svelte"; 16 | import { scss } from "svelte-preprocess"; 17 | import livereload from "rollup-plugin-livereload"; 18 | import css from "rollup-plugin-css-only"; 19 | import { spawn } from "child_process"; 20 | 21 | export async function createRollupConfig( 22 | opts: SvereOptions, 23 | outputNum: number 24 | ): Promise { 25 | const shouldMinify = 26 | opts.minify !== undefined ? opts.minify : opts.env === "production"; 27 | 28 | const outputName = 29 | opts.commandName === "start" 30 | ? "public/bundle.js" 31 | : [ 32 | `${paths.appDist}/${safePackageName(opts.name)}`, 33 | opts.format, 34 | opts.env, 35 | shouldMinify ? "min" : "", 36 | "js" 37 | ] 38 | .filter(Boolean) 39 | .join("."); 40 | 41 | const tsconfigPath = opts.tsconfig || paths.tsconfigJson; 42 | // borrowed from https://github.com/facebook/create-react-app/pull/7248 43 | const tsconfigJSON = ts.readConfigFile(tsconfigPath, ts.sys.readFile).config; 44 | // borrowed from https://github.com/ezolenko/rollup-plugin-typescript2/blob/42173460541b0c444326bf14f2c8c27269c4cb11/src/parse-tsconfig.ts#L48 45 | const tsCompilerOptions = ts.parseJsonConfigFileContent( 46 | tsconfigJSON, 47 | ts.sys, 48 | "./" 49 | ).options; 50 | 51 | const serve = () => { 52 | let server; 53 | 54 | function toExit() { 55 | if (server) server.kill(0); 56 | } 57 | 58 | return { 59 | async writeBundle() { 60 | if (server) return; 61 | server = spawn( 62 | "npx", 63 | ["sirv public", "--host", "--dev", "--port", `${opts.port}`], 64 | { 65 | stdio: ["ignore", "inherit", "inherit"], 66 | shell: true 67 | } 68 | ); 69 | if (!opts.silent) { 70 | setTimeout(() => { 71 | spawn("open", [`http://0.0.0.0:${opts.port}`], { 72 | stdio: ["ignore", "inherit", "inherit"], 73 | shell: true 74 | }); 75 | }, 500); 76 | } 77 | process.on("SIGTERM", toExit); 78 | process.on("exit", toExit); 79 | } 80 | }; 81 | }; 82 | 83 | const plugins = [].concat( 84 | opts.commandName === "start" 85 | ? [ 86 | svelte({ 87 | compilerOptions: { 88 | dev: true 89 | }, 90 | preprocess: [ 91 | scss({ 92 | /** options */ 93 | }) 94 | ] 95 | }), 96 | // we'll extract any component CSS out into 97 | // a separate file - better for performance 98 | css({ output: "bundle.css" }), 99 | 100 | // If you have external dependencies installed from 101 | // npm, you'll most likely need these plugins. In 102 | // some cases you'll need additional configuration - 103 | // consult the documentation for details: 104 | // https://github.com/rollup/plugins/tree/master/packages/commonjs 105 | resolve({ 106 | browser: true, 107 | dedupe: ["svelte"] 108 | }), 109 | commonjs(), 110 | serve(), 111 | livereload("public") 112 | ] 113 | : [ 114 | svelte({ 115 | preprocess: [ 116 | scss({ 117 | /** options */ 118 | }) 119 | ], 120 | emitCss: false 121 | }), 122 | resolve({ 123 | mainFields: [ 124 | "module", 125 | "main", 126 | opts.target !== "node" ? "browser" : undefined 127 | ].filter(Boolean) as string[], 128 | extensions: [...RESOLVE_DEFAULTS.extensions, ".svelte"] 129 | }), 130 | // all bundled external modules need to be converted from CJS to ESM 131 | commonjs({ 132 | // use a regex to make sure to include eventual hoisted packages 133 | include: 134 | opts.format === "umd" 135 | ? /\/node_modules\// 136 | : /\/regenerator-runtime\// 137 | }), 138 | json(), 139 | typescript({ 140 | typescript: ts, 141 | tsconfig: opts.tsconfig, 142 | tsconfigDefaults: { 143 | exclude: [ 144 | // all TS test files, regardless whether co-located or in test/ etc 145 | "**/*.spec.ts", 146 | "**/*.test.ts", 147 | "**/*.spec.tsx", 148 | "**/*.test.tsx", 149 | // TS defaults below 150 | "node_modules", 151 | "bower_components", 152 | "jspm_packages", 153 | paths.appDist 154 | ], 155 | compilerOptions: { 156 | sourceMap: true, 157 | declaration: true, 158 | jsx: "react" 159 | } 160 | }, 161 | tsconfigOverride: { 162 | compilerOptions: { 163 | // TS -> esnext, then leave the rest to babel-preset-env 164 | target: "esnext", 165 | // don't output declarations more than once 166 | ...(outputNum > 0 167 | ? { declaration: false, declarationMap: false } 168 | : {}) 169 | } 170 | }, 171 | check: !opts.transpileOnly && outputNum === 0, 172 | useTsconfigDeclarationDir: Boolean( 173 | tsCompilerOptions?.declarationDir 174 | ) 175 | }), 176 | opts.env !== undefined && 177 | replace({ 178 | preventAssignment: true, 179 | "process.env.NODE_ENV": JSON.stringify(opts.env) 180 | }), 181 | sourceMaps(), 182 | shouldMinify && 183 | terser({ 184 | output: { comments: false }, 185 | compress: { 186 | keep_infinity: true, 187 | pure_getters: true, 188 | passes: 10 189 | }, 190 | ecma: 5, 191 | toplevel: opts.format === "umd" 192 | }) 193 | ] 194 | ); 195 | 196 | return { 197 | external: ["svelte"], 198 | // Tell Rollup the entry point to the package 199 | input: opts.input, 200 | // Rollup has treeshaking by default, but we can optimize it further... 201 | treeshake: { 202 | propertyReadSideEffects: false 203 | }, 204 | // Establish Rollup output 205 | output: { 206 | // Set filenames of the consumer's package 207 | file: outputName, 208 | // Pass through the file format 209 | format: opts.format, 210 | // Do not let Rollup call Object.freeze() on namespace import objects 211 | // (i.e. import * as namespaceImportObject from...) that are accessed dynamically. 212 | freeze: false, 213 | // Respect tsconfig esModuleInterop when setting __esModule. 214 | esModule: Boolean(tsCompilerOptions?.esModuleInterop), 215 | name: opts.name || safeVariableName(opts.name), 216 | sourcemap: opts.commandName !== "start", 217 | exports: "named" 218 | }, 219 | plugins, 220 | watch: { 221 | clearScreen: true 222 | } 223 | }; 224 | } 225 | -------------------------------------------------------------------------------- /packages/core/README.md: -------------------------------------------------------------------------------- 1 | # SVERE CORE 2 | 3 | Adapters for svere core. 4 | 5 |

6 | PRs Welcome 7 | License 8 | Downloads 9 | 10 |

11 | 12 | English | [简体中文](https://github.com/FE-PIRL/svere/blob/master/packages/core/README_ZH.md) 13 | 14 | --- 15 | 16 | - [Intro](#intro) 17 | - [Install](#install) 18 | - [Usage](#usage) 19 | - [Examples](#examples) 20 | - [React](#react) 21 | - [Vue2](#vue2) 22 | - [Vue3](#vue3) 23 | - [Cli](#cli) 24 | - [Todos](#todos) 25 | 26 | # Intro 27 | 28 | Managing support for libraries that provide UI components across frameworks is a pain, 29 | especially when [Web Component](https://developer.mozilla.org/en-US/docs/Web/Web_Components) are not an option (e.g. for server side rendering, best performance, etc). 30 | 31 | At present, the [svelte](https://svelte.dev/) framework is developing rapidly. 32 | It is a good backward compatibility solution to make svelte components run in the old `react` or `vue` project, 33 | especially when the team's technology stack is not unified, this provides an idea of cross-framework sharing component. 34 | 35 | `Svere` contains several `adapters` for `React/Vue2/Vue3` which allows you to pass props and respond to events in a way that makes sense for that library. 36 | Also, it provides a [cli](https://github.com/FE-PIRL/svere/blob/master/packages/cli/README.md) to quickly create svelte components that can be shared across components. 37 | 38 | # Install 39 | 40 | With [npm](https://www.npmjs.com/): 41 | 42 | ```bash 43 | npm install @svere/core 44 | ``` 45 | 46 | Or with [yarn](https://yarnpkg.com/lang/en/): 47 | 48 | ```bash 49 | yarn add @svere/core 50 | ``` 51 | # Usage 52 | 53 | The core of svere exposes several adapter functions, namely `toReact`, `toVue` and `toVue3`. 54 | Each adapter is a simple function that takes a svelte component and a few options and returns a Vue or React component that can be used in Vue templates or JSX as you would expect. 55 | 56 | All adapters have the same signature as below, eg: 57 | ```ts 58 | toReact(Component: SvelteComponent, wrapperProps?: WrapperProps) : Component 59 | ``` 60 | - `Component` should be a compiled svelte component, either precompiled or compiled as part of your build step using rollup-plugin-svelte for rollup or svelte-loader from webpack. 61 | - `wrapperProps` (optional) should be an object contains wrapper `element`, `id`, `className` and `styles`. 62 | - `element` : all component have a base wrapper element, by default this is a `
` but you can pass in a string to customise this behaviour (eg: 'span', 'li', etc.) 63 | - `id` : add an id attribute to the base wrapper element, by default this is `svelte-wrapper`. 64 | - `className` : add a class attribute to the base wrapper element which you can define styles in your css files. 65 | - `styles` : add an inline styles attribute to the base wrapper element which can override the `className` attribute. 66 | 67 | # Examples 68 | 69 | In the examples below, the [svelte component](https://github.com/FE-PIRL/svere/blob/master/examples/src/Component.svelte) we will be using is a simple component that accepts a prop that will be rendered and emits an event upon clicking a button. 70 | Bundle it to a single file with `umd` format, then it will be imported by other framework conveniently. 71 | 72 | ```sveltehtml 73 | 88 | 89 |
90 |

Hello {name}, welcome!

91 | 94 | 97 |
98 | 99 | 114 | ``` 115 | 116 | ## React 117 | 118 | ```jsx 119 | import React, { useState } from "react"; 120 | import SvelteComponent from "./svelte-component"; 121 | import toReact from '@svere/core/dist/react.js' 122 | 123 | const wrapperProps = { 124 | element: "section", 125 | className: "section-css", 126 | id: "svelte-react", 127 | styles: { 128 | border: "1px solid gray", 129 | }, 130 | }; 131 | const ReactComponent = toReact(SvelteComponent, wrapperProps); 132 | 133 | const App = () => { 134 | const [name, setName] = useState('ben'); 135 | const changeName = () => setName(n => { 136 | return name === 'ben' ? 'yasin' : 'ben' 137 | }); 138 | 139 | const handleEventCallback = (e) => { 140 | console.log(e.detail) 141 | }; 142 | 143 | const handleWatchCallback = (name) => { 144 | console.log(name) 145 | }; 146 | 147 | return ( 148 |
149 | 154 | 155 |
156 | 157 | 158 |
159 | ); 160 | }; 161 | ``` 162 | 163 | ## Vue2 164 | 165 | ```vue 166 | 176 | 177 | 203 | ``` 204 | 205 | ## Vue3 206 | 207 | ```vue 208 | 217 | 218 | 255 | 256 | 261 | ``` 262 | 263 | # Cli 264 | Try svere out locally with our [CLI](https://github.com/FE-PIRL/svere/tree/master/packages/cli) 265 | 266 | With [npm](https://www.npmjs.com/): 267 | 268 | ```bash 269 | npm install @svere/cli 270 | ``` 271 | 272 | Or with [yarn](https://yarnpkg.com/lang/en/): 273 | 274 | ```bash 275 | yarn add @svere/cli 276 | ``` 277 | 278 | # Todos 279 | 280 | - develop core to add more features, eg: sub-component, slots. -------------------------------------------------------------------------------- /packages/cli/README.md: -------------------------------------------------------------------------------- 1 | # SVERE CLI 2 | 3 | A CLI for svere. 4 | 5 |

6 | PRs Welcome 7 | License 8 | Downloads 9 | 10 |

11 | 12 | English | [简体中文](https://github.com/FE-PIRL/svere/blob/master/packages/cli/README_ZH.md) 13 | 14 | --- 15 | 16 | - [Intro](#intro) 17 | - [Features](#features) 18 | - [Install](#install) 19 | - [Quick Start](#quick-start) 20 | - [Customization](#customization) 21 | - [Publishing](#publishing) 22 | - [API Reference](#api-reference) 23 | - [Author](#author) 24 | - [License](#license) 25 | 26 | # Intro 27 | 28 | Although you can build your own project to produce target files that meet the requirements, you still have a lot of work to do. Our goal is to help you get rid of the tedious work and let you concentrate on the development of business components. `Svere cli` is a zero-config scaffold, involving life cycles of component such as `development`, `packaging`, `syntax checking`, `testing`, `documentation` and `publishing`, to make you having a relaxed component development experience. 29 | 30 | # Features 31 | 32 | * Live reload and watch mode 33 | * Works with Typescript 34 | * Run crypress and Jest via `svere test` 35 | * ESLint and Stylelint with Prettier via `svere lint` 36 | * Run storybook via `svere doc` 37 | * Escape hatches for customization via `svere.config.js` and exported files of [code-specification-unid](https://github.com/FE-PIRL/code-specification-unid) 38 | 39 | # Install 40 | 41 | With [npm](https://www.npmjs.com/): 42 | 43 | ```bash 44 | npm install -g @svere/cli 45 | ``` 46 | 47 | Or with [yarn](https://yarnpkg.com/lang/en/): 48 | 49 | ```bash 50 | yarn add global @svere/cli 51 | ``` 52 | 53 | # Quick Start 54 | 55 | ```bash 56 | svere create mycom 57 | cd mycom 58 | yarn start 59 | ``` 60 | 61 | That's it. You don't need to worry about setting up TypeScript or Rollup or Jest or other plumbing. 62 | Just start editing `src/components/MyComponent.svelte` and go! 63 | 64 | Below is a list of commands you will probably find useful: 65 | 66 | ```npm start``` or ```yarn start``` 67 | 68 | Runs the project in development/watch mode. Your project will be rebuilt upon changes. 69 | 70 | ```npm run build``` or ```yarn build``` 71 | 72 | Bundles the package to the dist folder. The package is optimized and bundled with Rollup into multiple formats (UMD, and ES Module). 73 | 74 | ```npm test``` or ```yarn test``` 75 | 76 | This template uses [Cypress](https://www.cypress.io/) & [testing-library](https://testing-library.com/docs/cypress-testing-library/intro/) for testing. 77 | 78 | It is highly recommended going through their docs if you intend on testing your components. 79 | 80 | You can witness a simple example by running `svere test -co`. 81 | 82 | ```npm run lint``` or ```yarn lint``` 83 | 84 | This template integrates with [code-specification-unid](https://github.com/FE-PIRL/code-specification-unid) for linting. 85 | 86 | It is highly recommended going through their docs if you intend on linting your components. 87 | 88 | The default command without options will run Eslint and Stylelint with Prettier. 89 | 90 | ```npm run doc``` or ```yarn doc``` 91 | 92 | This template integrates with [Storybook](https://storybook.js.org/) for docs. 93 | 94 | You can generate static doc files by runing `svere doc -b`. 95 | 96 | 97 | # Customization 98 | 99 | ### Rollup 100 | 101 | > **❗⚠️❗ Warning**:
102 | > These modifications will override the default behavior and configuration of SVERE. As such they can invalidate internal guarantees and assumptions. These types of changes can break internal behavior and can be very fragile against updates. Use with discretion! 103 | 104 | SVERE uses Rollup under the hood. The defaults are solid for most packages (Formik uses the defaults!). However, if you do wish to alter the rollup configuration, you can do so by creating a file called `svere.config.js` at the root of your project like so: 105 | 106 | ```js 107 | // Not transpiled with TypeScript or Babel, so use plain Es6/Node.js! 108 | module.exports = { 109 | // This function will run for each entry/format/env combination 110 | rollup(config, options) { 111 | return config; // always return a config. 112 | }, 113 | }; 114 | ``` 115 | 116 | The `options` object contains the following: 117 | 118 | ```tsx 119 | export interface SvereOptions { 120 | // Name of package 121 | name: string; 122 | // Port for dev 123 | port: number; 124 | // path to file 125 | input: string; 126 | // never launch browser automatically 127 | silent: boolean; 128 | // Module format 129 | format: 'umd' | 'esm' | 'iife'; 130 | // Environment 131 | env: 'development' | 'production'; 132 | // Path to tsconfig file 133 | tsconfig?: string; 134 | // Is error extraction running? 135 | extractErrors?: boolean; 136 | // Is minifying? 137 | minify?: boolean; 138 | // Is this the very first rollup config (and thus should one-off metadata be extracted)? 139 | writeMeta?: boolean; 140 | // Only transpile, do not type check (makes compilation faster) 141 | transpileOnly?: boolean; 142 | } 143 | ``` 144 | 145 | #### Example: Adding Postcss 146 | 147 | ```js 148 | const postcss = require('rollup-plugin-postcss'); 149 | const autoprefixer = require('autoprefixer'); 150 | const cssnano = require('cssnano'); 151 | 152 | module.exports = { 153 | rollup(config, options) { 154 | config.plugins.push( 155 | postcss({ 156 | plugins: [ 157 | autoprefixer(), 158 | cssnano({ 159 | preset: 'default', 160 | }), 161 | ], 162 | inject: false, 163 | // only write out CSS for the first bundle (avoids pointless extra files): 164 | extract: !!options.writeMeta, 165 | }) 166 | ); 167 | return config; 168 | }, 169 | }; 170 | ``` 171 | 172 | 173 | # Publishing 174 | 175 | 176 | # API Reference 177 | 178 | ### ```svere start``` 179 | 180 | ```bash 181 | Usage: svere start [options] 182 | 183 | start development server 184 | 185 | Options: 186 | --entry specify entry file for dev (default: "src/main.ts") 187 | -d, --debug more debug logging (default: false) 188 | -s, --silent never launch the browser (default: false) 189 | -p, --port specify port for dev (default: "5000") 190 | -h, --help display help for command 191 | ``` 192 | 193 | 194 | ### ```svere build``` 195 | 196 | ```bash 197 | Usage: svere build [options] 198 | 199 | Build your component once and exit 200 | 201 | Options: 202 | --entry specify entry file for build (default: "src/components/index.ts") 203 | --fileName specify fileName exposed in UMD builds 204 | --format specify module format(s) (default: "umd,esm") 205 | --transpileOnly skip type checking (default: true) 206 | -d, --debug more debug logging (default: false) 207 | -bsb, --buildStorybook build storybook to static files (default: false) 208 | -h, --help display help for command 209 | ``` 210 | 211 | 212 | ### ```svere test``` 213 | 214 | ```bash 215 | Usage: svere test [options] 216 | 217 | Run cypress and jest test runner 218 | 219 | Options: 220 | -co, --cypressOpen run cypress open (default: false) 221 | -p, --port specify port for test (default: "5000") 222 | -h, --help display help for command 223 | ``` 224 | 225 | 226 | ### ```svere lint``` 227 | 228 | ```bash 229 | Usage: svere lint [options] 230 | 231 | Run eslint and stylelint with prettier 232 | 233 | Options: 234 | -js, --js run eslint with prettier only (default: false) 235 | -css, --css run stylelint with prettier only (default: false) 236 | -f, --format run prettier only (default: false) 237 | -jfs, --jsFiles specify files for eslint (default: "src/**/*.{js,jsx,ts,tsx,svelte}") 238 | -cfs, --cssFiles specify files for stylelint (default: "src/**/*.{less,postcss,css,scss,svelte}") 239 | -ffs, --formatFiles specify files for prettier (default: "src/**/*.{js,json,ts,tsx,svelte,css,less,scss,html,md}") 240 | -h, --help display help for command 241 | ``` 242 | 243 | 244 | ### ```svere doc``` 245 | 246 | ```bash 247 | Usage: svere doc [options] 248 | 249 | Start storybook for component 250 | 251 | Options: 252 | -p, --port specify port to run storybook (default: "6006") 253 | -b, --build build storybook to static files (default: false) 254 | -h, --help display help for command 255 | ``` 256 | 257 | # Author 258 | 259 | [benyasin](https://github.com/benyasin) 260 | 261 | # License 262 | 263 | [MIT](https://oss.ninja/mit/jaredpalmer/) 264 | 265 | 266 | # Todos 267 | 268 | * Add a command to publish the component to npm or cdn 269 | -------------------------------------------------------------------------------- /examples/dist/index.mjs: -------------------------------------------------------------------------------- 1 | function noop() { } 2 | function run(fn) { 3 | return fn(); 4 | } 5 | function blank_object() { 6 | return Object.create(null); 7 | } 8 | function run_all(fns) { 9 | fns.forEach(run); 10 | } 11 | function is_function(thing) { 12 | return typeof thing === 'function'; 13 | } 14 | function safe_not_equal(a, b) { 15 | return a != a ? b == b : a !== b || ((a && typeof a === 'object') || typeof a === 'function'); 16 | } 17 | function is_empty(obj) { 18 | return Object.keys(obj).length === 0; 19 | } 20 | 21 | function append(target, node) { 22 | target.appendChild(node); 23 | } 24 | function insert(target, node, anchor) { 25 | target.insertBefore(node, anchor || null); 26 | } 27 | function detach(node) { 28 | node.parentNode.removeChild(node); 29 | } 30 | function element(name) { 31 | return document.createElement(name); 32 | } 33 | function text(data) { 34 | return document.createTextNode(data); 35 | } 36 | function space() { 37 | return text(' '); 38 | } 39 | function listen(node, event, handler, options) { 40 | node.addEventListener(event, handler, options); 41 | return () => node.removeEventListener(event, handler, options); 42 | } 43 | function attr(node, attribute, value) { 44 | if (value == null) 45 | node.removeAttribute(attribute); 46 | else if (node.getAttribute(attribute) !== value) 47 | node.setAttribute(attribute, value); 48 | } 49 | function children(element) { 50 | return Array.from(element.childNodes); 51 | } 52 | function set_data(text, data) { 53 | data = '' + data; 54 | if (text.wholeText !== data) 55 | text.data = data; 56 | } 57 | function custom_event(type, detail) { 58 | const e = document.createEvent('CustomEvent'); 59 | e.initCustomEvent(type, false, false, detail); 60 | return e; 61 | } 62 | 63 | let current_component; 64 | function set_current_component(component) { 65 | current_component = component; 66 | } 67 | function get_current_component() { 68 | if (!current_component) 69 | throw new Error('Function called outside component initialization'); 70 | return current_component; 71 | } 72 | function createEventDispatcher() { 73 | const component = get_current_component(); 74 | return (type, detail) => { 75 | const callbacks = component.$$.callbacks[type]; 76 | if (callbacks) { 77 | // TODO are there situations where events could be dispatched 78 | // in a server (non-DOM) environment? 79 | const event = custom_event(type, detail); 80 | callbacks.slice().forEach(fn => { 81 | fn.call(component, event); 82 | }); 83 | } 84 | }; 85 | } 86 | 87 | const dirty_components = []; 88 | const binding_callbacks = []; 89 | const render_callbacks = []; 90 | const flush_callbacks = []; 91 | const resolved_promise = Promise.resolve(); 92 | let update_scheduled = false; 93 | function schedule_update() { 94 | if (!update_scheduled) { 95 | update_scheduled = true; 96 | resolved_promise.then(flush); 97 | } 98 | } 99 | function add_render_callback(fn) { 100 | render_callbacks.push(fn); 101 | } 102 | let flushing = false; 103 | const seen_callbacks = new Set(); 104 | function flush() { 105 | if (flushing) 106 | return; 107 | flushing = true; 108 | do { 109 | // first, call beforeUpdate functions 110 | // and update components 111 | for (let i = 0; i < dirty_components.length; i += 1) { 112 | const component = dirty_components[i]; 113 | set_current_component(component); 114 | update(component.$$); 115 | } 116 | set_current_component(null); 117 | dirty_components.length = 0; 118 | while (binding_callbacks.length) 119 | binding_callbacks.pop()(); 120 | // then, once components are updated, call 121 | // afterUpdate functions. This may cause 122 | // subsequent updates... 123 | for (let i = 0; i < render_callbacks.length; i += 1) { 124 | const callback = render_callbacks[i]; 125 | if (!seen_callbacks.has(callback)) { 126 | // ...so guard against infinite loops 127 | seen_callbacks.add(callback); 128 | callback(); 129 | } 130 | } 131 | render_callbacks.length = 0; 132 | } while (dirty_components.length); 133 | while (flush_callbacks.length) { 134 | flush_callbacks.pop()(); 135 | } 136 | update_scheduled = false; 137 | flushing = false; 138 | seen_callbacks.clear(); 139 | } 140 | function update($$) { 141 | if ($$.fragment !== null) { 142 | $$.update(); 143 | run_all($$.before_update); 144 | const dirty = $$.dirty; 145 | $$.dirty = [-1]; 146 | $$.fragment && $$.fragment.p($$.ctx, dirty); 147 | $$.after_update.forEach(add_render_callback); 148 | } 149 | } 150 | const outroing = new Set(); 151 | function transition_in(block, local) { 152 | if (block && block.i) { 153 | outroing.delete(block); 154 | block.i(local); 155 | } 156 | } 157 | function mount_component(component, target, anchor, customElement) { 158 | const { fragment, on_mount, on_destroy, after_update } = component.$$; 159 | fragment && fragment.m(target, anchor); 160 | if (!customElement) { 161 | // onMount happens before the initial afterUpdate 162 | add_render_callback(() => { 163 | const new_on_destroy = on_mount.map(run).filter(is_function); 164 | if (on_destroy) { 165 | on_destroy.push(...new_on_destroy); 166 | } 167 | else { 168 | // Edge case - component was destroyed immediately, 169 | // most likely as a result of a binding initialising 170 | run_all(new_on_destroy); 171 | } 172 | component.$$.on_mount = []; 173 | }); 174 | } 175 | after_update.forEach(add_render_callback); 176 | } 177 | function destroy_component(component, detaching) { 178 | const $$ = component.$$; 179 | if ($$.fragment !== null) { 180 | run_all($$.on_destroy); 181 | $$.fragment && $$.fragment.d(detaching); 182 | // TODO null out other refs, including component.$$ (but need to 183 | // preserve final state?) 184 | $$.on_destroy = $$.fragment = null; 185 | $$.ctx = []; 186 | } 187 | } 188 | function make_dirty(component, i) { 189 | if (component.$$.dirty[0] === -1) { 190 | dirty_components.push(component); 191 | schedule_update(); 192 | component.$$.dirty.fill(0); 193 | } 194 | component.$$.dirty[(i / 31) | 0] |= (1 << (i % 31)); 195 | } 196 | function init(component, options, instance, create_fragment, not_equal, props, dirty = [-1]) { 197 | const parent_component = current_component; 198 | set_current_component(component); 199 | const $$ = component.$$ = { 200 | fragment: null, 201 | ctx: null, 202 | // state 203 | props, 204 | update: noop, 205 | not_equal, 206 | bound: blank_object(), 207 | // lifecycle 208 | on_mount: [], 209 | on_destroy: [], 210 | on_disconnect: [], 211 | before_update: [], 212 | after_update: [], 213 | context: new Map(parent_component ? parent_component.$$.context : options.context || []), 214 | // everything else 215 | callbacks: blank_object(), 216 | dirty, 217 | skip_bound: false 218 | }; 219 | let ready = false; 220 | $$.ctx = instance 221 | ? instance(component, options.props || {}, (i, ret, ...rest) => { 222 | const value = rest.length ? rest[0] : ret; 223 | if ($$.ctx && not_equal($$.ctx[i], $$.ctx[i] = value)) { 224 | if (!$$.skip_bound && $$.bound[i]) 225 | $$.bound[i](value); 226 | if (ready) 227 | make_dirty(component, i); 228 | } 229 | return ret; 230 | }) 231 | : []; 232 | $$.update(); 233 | ready = true; 234 | run_all($$.before_update); 235 | // `false` as a special case of no DOM component 236 | $$.fragment = create_fragment ? create_fragment($$.ctx) : false; 237 | if (options.target) { 238 | if (options.hydrate) { 239 | const nodes = children(options.target); 240 | // eslint-disable-next-line @typescript-eslint/no-non-null-assertion 241 | $$.fragment && $$.fragment.l(nodes); 242 | nodes.forEach(detach); 243 | } 244 | else { 245 | // eslint-disable-next-line @typescript-eslint/no-non-null-assertion 246 | $$.fragment && $$.fragment.c(); 247 | } 248 | if (options.intro) 249 | transition_in(component.$$.fragment); 250 | mount_component(component, options.target, options.anchor, options.customElement); 251 | flush(); 252 | } 253 | set_current_component(parent_component); 254 | } 255 | /** 256 | * Base class for Svelte components. Used when dev=false. 257 | */ 258 | class SvelteComponent { 259 | $destroy() { 260 | destroy_component(this, 1); 261 | this.$destroy = noop; 262 | } 263 | $on(type, callback) { 264 | const callbacks = (this.$$.callbacks[type] || (this.$$.callbacks[type] = [])); 265 | callbacks.push(callback); 266 | return () => { 267 | const index = callbacks.indexOf(callback); 268 | if (index !== -1) 269 | callbacks.splice(index, 1); 270 | }; 271 | } 272 | $set($$props) { 273 | if (this.$$set && !is_empty($$props)) { 274 | this.$$.skip_bound = true; 275 | this.$$set($$props); 276 | this.$$.skip_bound = false; 277 | } 278 | } 279 | } 280 | 281 | /* src/Component.svelte generated by Svelte v3.38.2 */ 282 | 283 | function add_css() { 284 | var style = element("style"); 285 | style.id = "svelte-4hq7sh-style"; 286 | style.textContent = "main.svelte-4hq7sh{text-align:center;padding:1em;max-width:240px;margin:0 auto}h1.svelte-4hq7sh{color:#ff3e00;text-transform:uppercase;font-size:4em;font-weight:100}"; 287 | append(document.head, style); 288 | } 289 | 290 | function create_fragment(ctx) { 291 | let main; 292 | let h1; 293 | let t0; 294 | let t1; 295 | let t2; 296 | let t3; 297 | let button0; 298 | let t4; 299 | let t5; 300 | let t6; 301 | let button1; 302 | let t7; 303 | let t8; 304 | let mounted; 305 | let dispose; 306 | 307 | return { 308 | c() { 309 | main = element("main"); 310 | h1 = element("h1"); 311 | t0 = text("Hello "); 312 | t1 = text(/*name*/ ctx[0]); 313 | t2 = text("!"); 314 | t3 = space(); 315 | button0 = element("button"); 316 | t4 = text("add count: "); 317 | t5 = text(/*count*/ ctx[1]); 318 | t6 = space(); 319 | button1 = element("button"); 320 | t7 = text("update name: "); 321 | t8 = text(/*name*/ ctx[0]); 322 | attr(h1, "class", "svelte-4hq7sh"); 323 | attr(main, "class", "svelte-4hq7sh"); 324 | }, 325 | m(target, anchor) { 326 | insert(target, main, anchor); 327 | append(main, h1); 328 | append(h1, t0); 329 | append(h1, t1); 330 | append(h1, t2); 331 | append(main, t3); 332 | append(main, button0); 333 | append(button0, t4); 334 | append(button0, t5); 335 | append(main, t6); 336 | append(main, button1); 337 | append(button1, t7); 338 | append(button1, t8); 339 | 340 | if (!mounted) { 341 | dispose = [ 342 | listen(button0, "click", /*handleChangeCount*/ ctx[2]), 343 | listen(button1, "click", /*handleChangeName*/ ctx[3]) 344 | ]; 345 | 346 | mounted = true; 347 | } 348 | }, 349 | p(ctx, [dirty]) { 350 | if (dirty & /*name*/ 1) set_data(t1, /*name*/ ctx[0]); 351 | if (dirty & /*count*/ 2) set_data(t5, /*count*/ ctx[1]); 352 | if (dirty & /*name*/ 1) set_data(t8, /*name*/ ctx[0]); 353 | }, 354 | i: noop, 355 | o: noop, 356 | d(detaching) { 357 | if (detaching) detach(main); 358 | mounted = false; 359 | run_all(dispose); 360 | } 361 | }; 362 | } 363 | 364 | function instance($$self, $$props, $$invalidate) { 365 | const dispatch = createEventDispatcher(); 366 | let { name } = $$props; 367 | let count = 0; 368 | 369 | function handleChangeCount(event) { 370 | $$invalidate(1, count += 1); 371 | dispatch("someEvent", count); 372 | } 373 | 374 | function handleChangeName() { 375 | $$invalidate(0, name = "boss"); 376 | } 377 | 378 | $$self.$$set = $$props => { 379 | if ("name" in $$props) $$invalidate(0, name = $$props.name); 380 | }; 381 | 382 | return [name, count, handleChangeCount, handleChangeName]; 383 | } 384 | 385 | class Component extends SvelteComponent { 386 | constructor(options) { 387 | super(); 388 | if (!document.getElementById("svelte-4hq7sh-style")) add_css(); 389 | init(this, options, instance, create_fragment, safe_not_equal, { name: 0 }); 390 | } 391 | } 392 | 393 | export default Component; 394 | -------------------------------------------------------------------------------- /examples/dist/index.js: -------------------------------------------------------------------------------- 1 | (function (global, factory) { 2 | typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : 3 | typeof define === 'function' && define.amd ? define(factory) : 4 | (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Examples = factory()); 5 | }(this, (function () { 'use strict'; 6 | 7 | function noop() { } 8 | function run(fn) { 9 | return fn(); 10 | } 11 | function blank_object() { 12 | return Object.create(null); 13 | } 14 | function run_all(fns) { 15 | fns.forEach(run); 16 | } 17 | function is_function(thing) { 18 | return typeof thing === 'function'; 19 | } 20 | function safe_not_equal(a, b) { 21 | return a != a ? b == b : a !== b || ((a && typeof a === 'object') || typeof a === 'function'); 22 | } 23 | function is_empty(obj) { 24 | return Object.keys(obj).length === 0; 25 | } 26 | 27 | function append(target, node) { 28 | target.appendChild(node); 29 | } 30 | function insert(target, node, anchor) { 31 | target.insertBefore(node, anchor || null); 32 | } 33 | function detach(node) { 34 | node.parentNode.removeChild(node); 35 | } 36 | function element(name) { 37 | return document.createElement(name); 38 | } 39 | function text(data) { 40 | return document.createTextNode(data); 41 | } 42 | function space() { 43 | return text(' '); 44 | } 45 | function listen(node, event, handler, options) { 46 | node.addEventListener(event, handler, options); 47 | return () => node.removeEventListener(event, handler, options); 48 | } 49 | function attr(node, attribute, value) { 50 | if (value == null) 51 | node.removeAttribute(attribute); 52 | else if (node.getAttribute(attribute) !== value) 53 | node.setAttribute(attribute, value); 54 | } 55 | function children(element) { 56 | return Array.from(element.childNodes); 57 | } 58 | function set_data(text, data) { 59 | data = '' + data; 60 | if (text.wholeText !== data) 61 | text.data = data; 62 | } 63 | function custom_event(type, detail) { 64 | const e = document.createEvent('CustomEvent'); 65 | e.initCustomEvent(type, false, false, detail); 66 | return e; 67 | } 68 | 69 | let current_component; 70 | function set_current_component(component) { 71 | current_component = component; 72 | } 73 | function get_current_component() { 74 | if (!current_component) 75 | throw new Error('Function called outside component initialization'); 76 | return current_component; 77 | } 78 | function createEventDispatcher() { 79 | const component = get_current_component(); 80 | return (type, detail) => { 81 | const callbacks = component.$$.callbacks[type]; 82 | if (callbacks) { 83 | // TODO are there situations where events could be dispatched 84 | // in a server (non-DOM) environment? 85 | const event = custom_event(type, detail); 86 | callbacks.slice().forEach(fn => { 87 | fn.call(component, event); 88 | }); 89 | } 90 | }; 91 | } 92 | 93 | const dirty_components = []; 94 | const binding_callbacks = []; 95 | const render_callbacks = []; 96 | const flush_callbacks = []; 97 | const resolved_promise = Promise.resolve(); 98 | let update_scheduled = false; 99 | function schedule_update() { 100 | if (!update_scheduled) { 101 | update_scheduled = true; 102 | resolved_promise.then(flush); 103 | } 104 | } 105 | function add_render_callback(fn) { 106 | render_callbacks.push(fn); 107 | } 108 | let flushing = false; 109 | const seen_callbacks = new Set(); 110 | function flush() { 111 | if (flushing) 112 | return; 113 | flushing = true; 114 | do { 115 | // first, call beforeUpdate functions 116 | // and update components 117 | for (let i = 0; i < dirty_components.length; i += 1) { 118 | const component = dirty_components[i]; 119 | set_current_component(component); 120 | update(component.$$); 121 | } 122 | set_current_component(null); 123 | dirty_components.length = 0; 124 | while (binding_callbacks.length) 125 | binding_callbacks.pop()(); 126 | // then, once components are updated, call 127 | // afterUpdate functions. This may cause 128 | // subsequent updates... 129 | for (let i = 0; i < render_callbacks.length; i += 1) { 130 | const callback = render_callbacks[i]; 131 | if (!seen_callbacks.has(callback)) { 132 | // ...so guard against infinite loops 133 | seen_callbacks.add(callback); 134 | callback(); 135 | } 136 | } 137 | render_callbacks.length = 0; 138 | } while (dirty_components.length); 139 | while (flush_callbacks.length) { 140 | flush_callbacks.pop()(); 141 | } 142 | update_scheduled = false; 143 | flushing = false; 144 | seen_callbacks.clear(); 145 | } 146 | function update($$) { 147 | if ($$.fragment !== null) { 148 | $$.update(); 149 | run_all($$.before_update); 150 | const dirty = $$.dirty; 151 | $$.dirty = [-1]; 152 | $$.fragment && $$.fragment.p($$.ctx, dirty); 153 | $$.after_update.forEach(add_render_callback); 154 | } 155 | } 156 | const outroing = new Set(); 157 | function transition_in(block, local) { 158 | if (block && block.i) { 159 | outroing.delete(block); 160 | block.i(local); 161 | } 162 | } 163 | function mount_component(component, target, anchor, customElement) { 164 | const { fragment, on_mount, on_destroy, after_update } = component.$$; 165 | fragment && fragment.m(target, anchor); 166 | if (!customElement) { 167 | // onMount happens before the initial afterUpdate 168 | add_render_callback(() => { 169 | const new_on_destroy = on_mount.map(run).filter(is_function); 170 | if (on_destroy) { 171 | on_destroy.push(...new_on_destroy); 172 | } 173 | else { 174 | // Edge case - component was destroyed immediately, 175 | // most likely as a result of a binding initialising 176 | run_all(new_on_destroy); 177 | } 178 | component.$$.on_mount = []; 179 | }); 180 | } 181 | after_update.forEach(add_render_callback); 182 | } 183 | function destroy_component(component, detaching) { 184 | const $$ = component.$$; 185 | if ($$.fragment !== null) { 186 | run_all($$.on_destroy); 187 | $$.fragment && $$.fragment.d(detaching); 188 | // TODO null out other refs, including component.$$ (but need to 189 | // preserve final state?) 190 | $$.on_destroy = $$.fragment = null; 191 | $$.ctx = []; 192 | } 193 | } 194 | function make_dirty(component, i) { 195 | if (component.$$.dirty[0] === -1) { 196 | dirty_components.push(component); 197 | schedule_update(); 198 | component.$$.dirty.fill(0); 199 | } 200 | component.$$.dirty[(i / 31) | 0] |= (1 << (i % 31)); 201 | } 202 | function init(component, options, instance, create_fragment, not_equal, props, dirty = [-1]) { 203 | const parent_component = current_component; 204 | set_current_component(component); 205 | const $$ = component.$$ = { 206 | fragment: null, 207 | ctx: null, 208 | // state 209 | props, 210 | update: noop, 211 | not_equal, 212 | bound: blank_object(), 213 | // lifecycle 214 | on_mount: [], 215 | on_destroy: [], 216 | on_disconnect: [], 217 | before_update: [], 218 | after_update: [], 219 | context: new Map(parent_component ? parent_component.$$.context : options.context || []), 220 | // everything else 221 | callbacks: blank_object(), 222 | dirty, 223 | skip_bound: false 224 | }; 225 | let ready = false; 226 | $$.ctx = instance 227 | ? instance(component, options.props || {}, (i, ret, ...rest) => { 228 | const value = rest.length ? rest[0] : ret; 229 | if ($$.ctx && not_equal($$.ctx[i], $$.ctx[i] = value)) { 230 | if (!$$.skip_bound && $$.bound[i]) 231 | $$.bound[i](value); 232 | if (ready) 233 | make_dirty(component, i); 234 | } 235 | return ret; 236 | }) 237 | : []; 238 | $$.update(); 239 | ready = true; 240 | run_all($$.before_update); 241 | // `false` as a special case of no DOM component 242 | $$.fragment = create_fragment ? create_fragment($$.ctx) : false; 243 | if (options.target) { 244 | if (options.hydrate) { 245 | const nodes = children(options.target); 246 | // eslint-disable-next-line @typescript-eslint/no-non-null-assertion 247 | $$.fragment && $$.fragment.l(nodes); 248 | nodes.forEach(detach); 249 | } 250 | else { 251 | // eslint-disable-next-line @typescript-eslint/no-non-null-assertion 252 | $$.fragment && $$.fragment.c(); 253 | } 254 | if (options.intro) 255 | transition_in(component.$$.fragment); 256 | mount_component(component, options.target, options.anchor, options.customElement); 257 | flush(); 258 | } 259 | set_current_component(parent_component); 260 | } 261 | /** 262 | * Base class for Svelte components. Used when dev=false. 263 | */ 264 | class SvelteComponent { 265 | $destroy() { 266 | destroy_component(this, 1); 267 | this.$destroy = noop; 268 | } 269 | $on(type, callback) { 270 | const callbacks = (this.$$.callbacks[type] || (this.$$.callbacks[type] = [])); 271 | callbacks.push(callback); 272 | return () => { 273 | const index = callbacks.indexOf(callback); 274 | if (index !== -1) 275 | callbacks.splice(index, 1); 276 | }; 277 | } 278 | $set($$props) { 279 | if (this.$$set && !is_empty($$props)) { 280 | this.$$.skip_bound = true; 281 | this.$$set($$props); 282 | this.$$.skip_bound = false; 283 | } 284 | } 285 | } 286 | 287 | /* src/Component.svelte generated by Svelte v3.38.2 */ 288 | 289 | function add_css() { 290 | var style = element("style"); 291 | style.id = "svelte-4hq7sh-style"; 292 | style.textContent = "main.svelte-4hq7sh{text-align:center;padding:1em;max-width:240px;margin:0 auto}h1.svelte-4hq7sh{color:#ff3e00;text-transform:uppercase;font-size:4em;font-weight:100}"; 293 | append(document.head, style); 294 | } 295 | 296 | function create_fragment(ctx) { 297 | let main; 298 | let h1; 299 | let t0; 300 | let t1; 301 | let t2; 302 | let t3; 303 | let button0; 304 | let t4; 305 | let t5; 306 | let t6; 307 | let button1; 308 | let t7; 309 | let t8; 310 | let mounted; 311 | let dispose; 312 | 313 | return { 314 | c() { 315 | main = element("main"); 316 | h1 = element("h1"); 317 | t0 = text("Hello "); 318 | t1 = text(/*name*/ ctx[0]); 319 | t2 = text("!"); 320 | t3 = space(); 321 | button0 = element("button"); 322 | t4 = text("add count: "); 323 | t5 = text(/*count*/ ctx[1]); 324 | t6 = space(); 325 | button1 = element("button"); 326 | t7 = text("update name: "); 327 | t8 = text(/*name*/ ctx[0]); 328 | attr(h1, "class", "svelte-4hq7sh"); 329 | attr(main, "class", "svelte-4hq7sh"); 330 | }, 331 | m(target, anchor) { 332 | insert(target, main, anchor); 333 | append(main, h1); 334 | append(h1, t0); 335 | append(h1, t1); 336 | append(h1, t2); 337 | append(main, t3); 338 | append(main, button0); 339 | append(button0, t4); 340 | append(button0, t5); 341 | append(main, t6); 342 | append(main, button1); 343 | append(button1, t7); 344 | append(button1, t8); 345 | 346 | if (!mounted) { 347 | dispose = [ 348 | listen(button0, "click", /*handleChangeCount*/ ctx[2]), 349 | listen(button1, "click", /*handleChangeName*/ ctx[3]) 350 | ]; 351 | 352 | mounted = true; 353 | } 354 | }, 355 | p(ctx, [dirty]) { 356 | if (dirty & /*name*/ 1) set_data(t1, /*name*/ ctx[0]); 357 | if (dirty & /*count*/ 2) set_data(t5, /*count*/ ctx[1]); 358 | if (dirty & /*name*/ 1) set_data(t8, /*name*/ ctx[0]); 359 | }, 360 | i: noop, 361 | o: noop, 362 | d(detaching) { 363 | if (detaching) detach(main); 364 | mounted = false; 365 | run_all(dispose); 366 | } 367 | }; 368 | } 369 | 370 | function instance($$self, $$props, $$invalidate) { 371 | const dispatch = createEventDispatcher(); 372 | let { name } = $$props; 373 | let count = 0; 374 | 375 | function handleChangeCount(event) { 376 | $$invalidate(1, count += 1); 377 | dispatch("someEvent", count); 378 | } 379 | 380 | function handleChangeName() { 381 | $$invalidate(0, name = "boss"); 382 | } 383 | 384 | $$self.$$set = $$props => { 385 | if ("name" in $$props) $$invalidate(0, name = $$props.name); 386 | }; 387 | 388 | return [name, count, handleChangeCount, handleChangeName]; 389 | } 390 | 391 | class Component extends SvelteComponent { 392 | constructor(options) { 393 | super(); 394 | if (!document.getElementById("svelte-4hq7sh-style")) add_css(); 395 | init(this, options, instance, create_fragment, safe_not_equal, { name: 0 }); 396 | } 397 | } 398 | 399 | return Component; 400 | 401 | }))); 402 | --------------------------------------------------------------------------------