├── .eslintrc ├── .gitignore ├── demo ├── .browserslistrc ├── public │ ├── robots.txt │ ├── favicon.ico │ ├── img │ │ └── icons │ │ │ ├── favicon-16x16.png │ │ │ ├── favicon-32x32.png │ │ │ ├── mstile-150x150.png │ │ │ ├── apple-touch-icon.png │ │ │ ├── android-chrome-192x192.png │ │ │ ├── android-chrome-512x512.png │ │ │ ├── apple-touch-icon-120x120.png │ │ │ ├── apple-touch-icon-152x152.png │ │ │ ├── apple-touch-icon-180x180.png │ │ │ ├── apple-touch-icon-60x60.png │ │ │ ├── apple-touch-icon-76x76.png │ │ │ ├── msapplication-icon-144x144.png │ │ │ └── safari-pinned-tab.svg │ ├── manifest.json │ └── index.html ├── babel.config.js ├── postcss.config.js ├── src │ ├── shims-vue.d.ts │ ├── assets │ │ └── demo-print.png │ ├── store.ts │ ├── router.ts │ ├── main.ts │ ├── shims-tsx.d.ts │ ├── components │ │ ├── Editor.vue │ │ └── Presets.vue │ ├── App.vue │ ├── registerServiceWorker.ts │ ├── utils │ │ └── presets.ts │ └── views │ │ └── Home.vue ├── vue.config.js ├── .editorconfig ├── .gitignore ├── .eslintrc.js ├── README.md ├── tsconfig.json └── package.json ├── src ├── index.ts ├── SvelteComponent.ts ├── Compiler.ts └── TemplateCompiler.ts ├── test ├── output │ ├── static.svelte │ └── simple.svelte └── index.spec.ts ├── jest.config.js ├── tsconfig.json ├── tslint.json ├── release-it.json ├── webpack.config.js ├── LICENSE ├── .circleci └── config.yml ├── package.json └── README.md /.eslintrc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist/* 3 | -------------------------------------------------------------------------------- /demo/.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | -------------------------------------------------------------------------------- /demo/public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: 3 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export { ComponentCompiler as default } from './Compiler' 2 | -------------------------------------------------------------------------------- /test/output/static.svelte: -------------------------------------------------------------------------------- 1 | I am just 2 |

STATIC

-------------------------------------------------------------------------------- /demo/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/app' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: 'ts-jest', 3 | testEnvironment: 'node', 4 | }; -------------------------------------------------------------------------------- /demo/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | autoprefixer: {} 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /demo/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trickstival/vue2svelte/HEAD/demo/public/favicon.ico -------------------------------------------------------------------------------- /demo/src/shims-vue.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.vue' { 2 | import Vue from 'vue' 3 | export default Vue 4 | } 5 | -------------------------------------------------------------------------------- /demo/src/assets/demo-print.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trickstival/vue2svelte/HEAD/demo/src/assets/demo-print.png -------------------------------------------------------------------------------- /demo/vue.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | configureWebpack: { 3 | node: { 4 | module: 'empty' 5 | } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /demo/public/img/icons/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trickstival/vue2svelte/HEAD/demo/public/img/icons/favicon-16x16.png -------------------------------------------------------------------------------- /demo/public/img/icons/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trickstival/vue2svelte/HEAD/demo/public/img/icons/favicon-32x32.png -------------------------------------------------------------------------------- /demo/public/img/icons/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trickstival/vue2svelte/HEAD/demo/public/img/icons/mstile-150x150.png -------------------------------------------------------------------------------- /demo/public/img/icons/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trickstival/vue2svelte/HEAD/demo/public/img/icons/apple-touch-icon.png -------------------------------------------------------------------------------- /demo/public/img/icons/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trickstival/vue2svelte/HEAD/demo/public/img/icons/android-chrome-192x192.png -------------------------------------------------------------------------------- /demo/public/img/icons/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trickstival/vue2svelte/HEAD/demo/public/img/icons/android-chrome-512x512.png -------------------------------------------------------------------------------- /demo/public/img/icons/apple-touch-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trickstival/vue2svelte/HEAD/demo/public/img/icons/apple-touch-icon-120x120.png -------------------------------------------------------------------------------- /demo/public/img/icons/apple-touch-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trickstival/vue2svelte/HEAD/demo/public/img/icons/apple-touch-icon-152x152.png -------------------------------------------------------------------------------- /demo/public/img/icons/apple-touch-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trickstival/vue2svelte/HEAD/demo/public/img/icons/apple-touch-icon-180x180.png -------------------------------------------------------------------------------- /demo/public/img/icons/apple-touch-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trickstival/vue2svelte/HEAD/demo/public/img/icons/apple-touch-icon-60x60.png -------------------------------------------------------------------------------- /demo/public/img/icons/apple-touch-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trickstival/vue2svelte/HEAD/demo/public/img/icons/apple-touch-icon-76x76.png -------------------------------------------------------------------------------- /demo/.editorconfig: -------------------------------------------------------------------------------- 1 | [*.{js,jsx,ts,tsx,vue}] 2 | indent_style = space 3 | indent_size = 2 4 | trim_trailing_whitespace = true 5 | insert_final_newline = true 6 | -------------------------------------------------------------------------------- /demo/public/img/icons/msapplication-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trickstival/vue2svelte/HEAD/demo/public/img/icons/msapplication-icon-144x144.png -------------------------------------------------------------------------------- /test/output/simple.svelte: -------------------------------------------------------------------------------- 1 |

{#if heyProp==='ai'}sup {heyProp}{/if} plp {heyProp} {hey}

-------------------------------------------------------------------------------- /demo/src/store.ts: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | 4 | Vue.use(Vuex) 5 | 6 | export default new Vuex.Store({ 7 | state: { 8 | 9 | }, 10 | mutations: { 11 | 12 | }, 13 | actions: { 14 | 15 | } 16 | }) 17 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "./dist/", 4 | "noImplicitAny": true, 5 | "module": "es6", 6 | "moduleResolution": "node", 7 | "target": "es5", 8 | "sourceMap": true, 9 | "declaration": true 10 | } 11 | } -------------------------------------------------------------------------------- /demo/src/router.ts: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Router from 'vue-router' 3 | import Home from './views/Home.vue' 4 | 5 | Vue.use(Router) 6 | 7 | export default new Router({ 8 | routes: [ 9 | { 10 | path: '/', 11 | name: 'home', 12 | component: Home 13 | } 14 | ] 15 | }) 16 | -------------------------------------------------------------------------------- /demo/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | # local env files 6 | .env.local 7 | .env.*.local 8 | 9 | # Log files 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | 14 | # Editor directories and files 15 | .idea 16 | .vscode 17 | *.suo 18 | *.ntvs* 19 | *.njsproj 20 | *.sln 21 | *.sw? 22 | -------------------------------------------------------------------------------- /demo/src/main.ts: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from './App.vue' 3 | import router from './router' 4 | import store from './store' 5 | import './registerServiceWorker' 6 | import 'prismjs' 7 | import 'prismjs/themes/prism.css' 8 | 9 | Vue.config.productionTip = false 10 | 11 | new Vue({ 12 | router, 13 | store, 14 | render: h => h(App) 15 | }).$mount('#app') 16 | -------------------------------------------------------------------------------- /demo/src/shims-tsx.d.ts: -------------------------------------------------------------------------------- 1 | import Vue, { VNode } from 'vue' 2 | 3 | declare global { 4 | namespace JSX { 5 | // tslint:disable no-empty-interface 6 | interface Element extends VNode {} 7 | // tslint:disable no-empty-interface 8 | interface ElementClass extends Vue {} 9 | interface IntrinsicElements { 10 | [elem: string]: any 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "defaultSeverity": "error", 3 | "extends": [ 4 | "tslint:recommended" 5 | ], 6 | "jsRules": {}, 7 | "rules": { 8 | "semicolon": [true, "never"], 9 | "space-before-function-paren": [true, "always"], 10 | "quotemark": [true, "single"], 11 | "interface-name": false 12 | }, 13 | "rulesDirectory": [] 14 | } -------------------------------------------------------------------------------- /release-it.json: -------------------------------------------------------------------------------- 1 | { 2 | "src": { 3 | "tagName": "v%s", 4 | "commitMessage": "chore(release): %s" 5 | }, 6 | "github": { 7 | "release": true, 8 | "releaseName": "🚀 Release %s", 9 | "tokenRef": "GITHUB_TOKEN" 10 | }, 11 | "npm": { 12 | "publish": true 13 | }, 14 | "changelogCommand": "git log --pretty=format:'* %s (%h)' [REV_RANGE]" 15 | } -------------------------------------------------------------------------------- /demo/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | node: true 5 | }, 6 | 'extends': [ 7 | 'plugin:vue/essential', 8 | '@vue/standard', 9 | '@vue/typescript' 10 | ], 11 | rules: { 12 | 'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off', 13 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off' 14 | }, 15 | parserOptions: { 16 | parser: '@typescript-eslint/parser' 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | entry: './src/index.ts', 5 | devtool: 'inline-source-map', 6 | module: { 7 | rules: [ 8 | { 9 | test: /\.ts$/, 10 | use: 'ts-loader', 11 | exclude: /node_modules/ 12 | } 13 | ] 14 | }, 15 | resolve: { 16 | extensions: [ '.ts', '.js' ] 17 | }, 18 | output: { 19 | filename: 'bundle.js', 20 | path: path.resolve(__dirname, 'dist') 21 | } 22 | } -------------------------------------------------------------------------------- /demo/README.md: -------------------------------------------------------------------------------- 1 | # demo 2 | 3 | ## Project setup 4 | ``` 5 | yarn install 6 | ``` 7 | 8 | ### Compiles and hot-reloads for development 9 | ``` 10 | yarn run serve 11 | ``` 12 | 13 | ### Compiles and minifies for production 14 | ``` 15 | yarn run build 16 | ``` 17 | 18 | ### Run your tests 19 | ``` 20 | yarn run test 21 | ``` 22 | 23 | ### Lints and fixes files 24 | ``` 25 | yarn run lint 26 | ``` 27 | 28 | ### Customize configuration 29 | See [Configuration Reference](https://cli.vuejs.org/config/). 30 | -------------------------------------------------------------------------------- /demo/src/components/Editor.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 21 | 22 | 27 | -------------------------------------------------------------------------------- /demo/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "demo", 3 | "short_name": "demo", 4 | "icons": [ 5 | { 6 | "src": "./img/icons/android-chrome-192x192.png", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | }, 10 | { 11 | "src": "./img/icons/android-chrome-512x512.png", 12 | "sizes": "512x512", 13 | "type": "image/png" 14 | } 15 | ], 16 | "start_url": "./index.html", 17 | "display": "standalone", 18 | "background_color": "#000000", 19 | "theme_color": "#4DBA87" 20 | } 21 | -------------------------------------------------------------------------------- /demo/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Vue2Svelte Demo 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /demo/src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 32 | -------------------------------------------------------------------------------- /demo/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "jsx": "preserve", 6 | "importHelpers": true, 7 | "moduleResolution": "node", 8 | "experimentalDecorators": true, 9 | "esModuleInterop": true, 10 | "allowSyntheticDefaultImports": true, 11 | "sourceMap": true, 12 | "baseUrl": ".", 13 | "types": [ 14 | "webpack-env" 15 | ], 16 | "paths": { 17 | "@/*": [ 18 | "src/*" 19 | ] 20 | }, 21 | "lib": [ 22 | "esnext", 23 | "dom", 24 | "dom.iterable", 25 | "scripthost" 26 | ] 27 | }, 28 | "include": [ 29 | "src/**/*.ts", 30 | "src/**/*.tsx", 31 | "src/**/*.vue", 32 | "tests/**/*.ts", 33 | "tests/**/*.tsx" 34 | ], 35 | "exclude": [ 36 | "node_modules" 37 | ] 38 | } 39 | -------------------------------------------------------------------------------- /demo/src/components/Presets.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 28 | 29 | 42 | -------------------------------------------------------------------------------- /demo/src/registerServiceWorker.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | 3 | import { register } from 'register-service-worker' 4 | 5 | if (process.env.NODE_ENV === 'production') { 6 | register(`${process.env.BASE_URL}service-worker.js`, { 7 | ready () { 8 | console.log( 9 | 'App is being served from cache by a service worker.\n' + 10 | 'For more details, visit https://goo.gl/AFskqB' 11 | ) 12 | }, 13 | registered () { 14 | console.log('Service worker has been registered.') 15 | }, 16 | cached () { 17 | console.log('Content has been cached for offline use.') 18 | }, 19 | updatefound () { 20 | console.log('New content is downloading.') 21 | }, 22 | updated () { 23 | console.log('New content is available; please refresh.') 24 | }, 25 | offline () { 26 | console.log('No internet connection found. App is running in offline mode.') 27 | }, 28 | error (error) { 29 | console.error('Error during service worker registration:', error) 30 | } 31 | }) 32 | } 33 | -------------------------------------------------------------------------------- /demo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "demo", 3 | "version": "0.1.1", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build", 8 | "lint": "vue-cli-service lint" 9 | }, 10 | "dependencies": { 11 | "core-js": "^2.6.5", 12 | "prismjs": "^1.17.1", 13 | "register-service-worker": "^1.6.2", 14 | "vue": "^2.6.10", 15 | "vue-class-component": "^7.0.2", 16 | "vue-property-decorator": "^8.1.0", 17 | "vue-router": "^3.0.3", 18 | "vuex": "^3.0.1" 19 | }, 20 | "devDependencies": { 21 | "@vue/cli-plugin-babel": "^3.9.0", 22 | "@vue/cli-plugin-eslint": "^3.9.0", 23 | "@vue/cli-plugin-pwa": "^3.9.0", 24 | "@vue/cli-plugin-typescript": "^3.9.0", 25 | "@vue/cli-service": "^3.9.0", 26 | "@vue/eslint-config-standard": "^4.0.0", 27 | "@vue/eslint-config-typescript": "^4.0.0", 28 | "babel-eslint": "^10.0.1", 29 | "eslint": "^5.16.0", 30 | "eslint-plugin-vue": "^5.0.0", 31 | "node-sass": "^4.9.0", 32 | "sass-loader": "^7.1.0", 33 | "typescript": "^3.4.3", 34 | "vue-template-compiler": "^2.6.10" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2019 Patrick Stival Chaerke 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | # Javascript Node CircleCI 2.0 configuration file 2 | # 3 | # Check https://circleci.com/docs/2.0/language-javascript/ for more details 4 | # 5 | version: 2 6 | jobs: 7 | build: 8 | docker: 9 | # specify the version you desire here 10 | - image: circleci/node:8.10 11 | 12 | # Specify service dependencies here if necessary 13 | # CircleCI maintains a library of pre-built images 14 | # documented at https://circleci.com/docs/2.0/circleci-images/ 15 | # - image: circleci/mongo:3.4.4 16 | 17 | working_directory: ~/repo 18 | 19 | steps: 20 | - checkout 21 | 22 | # Download and cache dependencies 23 | - restore_cache: 24 | keys: 25 | - v1-dependencies-{{ checksum "package.json" }} 26 | # fallback to using the latest cache if no exact match is found 27 | - v1-dependencies- 28 | 29 | - run: yarn install 30 | 31 | - save_cache: 32 | paths: 33 | - node_modules 34 | key: v1-dependencies-{{ checksum "package.json" }} 35 | 36 | - run: yarn lint 37 | # run tests! 38 | - run: yarn test:unit 39 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue2svelte", 3 | "version": "0.0.11", 4 | "description": "A Vue SFC to Svelte Transpiler", 5 | "main": "dist/index.js", 6 | "author": "trickstival", 7 | "repository": { 8 | "url": "https://github.com/trickstival/vue2svelte" 9 | }, 10 | "license": "MIT", 11 | "files": [ 12 | "dist" 13 | ], 14 | "scripts": { 15 | "test:unit": "jest", 16 | "lint": "tslint -c tslint.json 'src/**/*.ts'", 17 | "dev": "jest --watchAll", 18 | "build": "webpack --mode=production" 19 | }, 20 | "types": "dist/src/index.d.ts", 21 | "devDependencies": { 22 | "@types/jest": "^24.0.15", 23 | "jest": "^24.8.0", 24 | "ts-jest": "^24.0.2", 25 | "ts-loader": "^6.0.4", 26 | "tslint": "^5.18.0", 27 | "typescript": "^3.5.2", 28 | "vue-template-compiler": "^2.6.10", 29 | "webpack": "^4.35.2", 30 | "webpack-cli": "^3.3.5" 31 | }, 32 | "dependencies": { 33 | "prismjs": "^1.17.1", 34 | "svelte": "^3.6.2", 35 | "vue": "^2.6.10", 36 | "vue-prism-editor": "^0.2.1" 37 | }, 38 | "keywords": [ 39 | "vue", 40 | "svelte", 41 | "compiler", 42 | "transpiler", 43 | "transform" 44 | ] 45 | } 46 | -------------------------------------------------------------------------------- /src/SvelteComponent.ts: -------------------------------------------------------------------------------- 1 | // This is an intermediary entity between vue and svelte 2 | 3 | export interface SvelteProp { 4 | name: string 5 | default: any 6 | } 7 | 8 | export interface SvelteData { 9 | name: string, 10 | initialValue: any, 11 | 12 | } 13 | 14 | export default class SvelteComponent { 15 | private props: SvelteProp[] 16 | private data: SvelteData[] 17 | private template: string 18 | 19 | constructor () { 20 | this.props = [] 21 | this.data = [] 22 | } 23 | public setTemplate (template: string) { 24 | this.template = template 25 | } 26 | public addProp (prop: SvelteProp) { 27 | this.props.push(prop) 28 | } 29 | public addData (data: SvelteData) { 30 | this.data.push(data) 31 | } 32 | public getCode () { 33 | const propsCode = this.props 34 | .map((prop) => `export let ${prop.name}=${JSON.stringify(prop.default)};`) 35 | .join('\n') 36 | 37 | const dataCode = this.data 38 | .map((data) => `let ${data.name}=${JSON.stringify(data.initialValue)};`) 39 | .join('\n') 40 | 41 | return `${this.template}` 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /demo/src/utils/presets.ts: -------------------------------------------------------------------------------- 1 | interface Preset { 2 | script: string 3 | template: string 4 | } 5 | 6 | const presets: Record = { 7 | default: { 8 | template: `
9 | 10 |
`, 11 | script: `{ 12 | props: { 13 | prop1: { 14 | default: 'yeeah Im a prop' 15 | } 16 | }, 17 | data() { 18 | return { 19 | data1: 'Just normal data' 20 | } 21 | } 22 | }` 23 | }, 24 | 'v-if and v-else': { 25 | template: `
26 | never gonna be shown 27 |
data1 shows
28 |
just v-else
29 |
`, 30 | script: `{ 31 | props: { 32 | prop1: { 33 | default: 'yeeah Im a prop' 34 | } 35 | }, 36 | data() { 37 | return { 38 | data1: 'Just normal data' 39 | } 40 | } 41 | }` 42 | }, 43 | 'v-for': { 44 | template: `
45 |

{{ idx }} {{ name }}

46 |
`, 47 | script: `{ 48 | data () { 49 | return { 50 | list: [{ name: 'Bob' }, { name: 'Dylan' }] 51 | } 52 | } 53 | }` 54 | }, 55 | 'print data': { 56 | template: `
57 | Printing prop: {{ prop1 }} 58 |
`, 59 | script: `{ 60 | props: { 61 | prop1: { 62 | default: 'yeeah Im a prop' 63 | } 64 | } 65 | }` 66 | } 67 | } 68 | 69 | export default presets 70 | -------------------------------------------------------------------------------- /test/index.spec.ts: -------------------------------------------------------------------------------- 1 | import Vue, { ComponentOptions, CreateElement } from 'vue'; 2 | import { ComponentCompiler } from '../src/Compiler'; 3 | import * as fs from 'fs' 4 | 5 | const getOutput = (filename: string) => fs.readFileSync(__dirname + `/output/${filename}.svelte`, 'utf-8') 6 | const compile = (options: ComponentOptions) => new ComponentCompiler(options).compile() 7 | 8 | describe('The Compiler', () => { 9 | it('Static html rendering', () => { 10 | const myComponent: ComponentOptions = { 11 | template: 'I am just \n

STATIC

' 12 | } 13 | 14 | const result = compile(myComponent) 15 | const expected = getOutput('static') 16 | expect(result).toBe(expected) 17 | }) 18 | it('Simple v-if and props compiling', () => { 19 | const myComponent: ComponentOptions = { 20 | template: `

sup {{ heyProp }} plp {{ heyProp }} {{ hey }}

`, 21 | props: { 22 | heyProp: { 23 | default: 'Im a prop' 24 | } 25 | }, 26 | data () { 27 | return { 28 | 29 | hey: 'Im hey data' 30 | } 31 | } 32 | } 33 | 34 | const compiler = new ComponentCompiler(myComponent) 35 | const result = compiler.compile() 36 | const expected = getOutput('simple') 37 | expect(result).toBe(expected) 38 | }) 39 | }) -------------------------------------------------------------------------------- /src/Compiler.ts: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import { ComponentOptions } from 'vue/types/options' 3 | import SvelteComponent, { SvelteData, SvelteProp } from './SvelteComponent' 4 | import TemplateCompiler from './TemplateCompiler' 5 | 6 | export class ComponentCompiler> { 7 | public vm: T 8 | public svelteComponent: SvelteComponent 9 | constructor (vm: T) { 10 | this.vm = vm 11 | this.svelteComponent = new SvelteComponent() 12 | } 13 | 14 | public compile (): string { 15 | this.mapProps() 16 | this.mapData() 17 | this.svelteComponent.setTemplate(new TemplateCompiler(this.vm).compile()) 18 | return this.svelteComponent.getCode() 19 | } 20 | 21 | public mapProps () { 22 | const props = this.vm.props || {} 23 | for (const [vuePropKey, vueProp = {}] of Object.entries(props)) { 24 | const svelteProp: SvelteProp = { 25 | default: vueProp.default, 26 | name: vuePropKey, 27 | } 28 | this.svelteComponent.addProp(svelteProp) 29 | } 30 | } 31 | 32 | public mapData () { 33 | let data = this.vm.data || {} 34 | data = typeof data === 'function' 35 | ? data() 36 | : data 37 | 38 | for (const [vueDataKey, vueData = {}] of Object.entries(data)) { 39 | const svelteData: SvelteData = { 40 | initialValue: vueData, 41 | name: vueDataKey, 42 | } 43 | this.svelteComponent.addData(svelteData) 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Vue2Svelte 2 | 3 | [Demo](https://blissful-goldstine-5db493.netlify.com) 4 | [![Demo Image](https://github.com/trickstival/vue2svelte/blob/master/demo/src/assets/demo-print.png?raw=true)](https://blissful-goldstine-5db493.netlify.com) 5 | 6 | Still under development and there's still a long way to go :o 7 | 8 | ## Install 9 | 10 | ```bash 11 | npm i vue2svelte 12 | # or 13 | yarn add vue2svelte 14 | ``` 15 | 16 | ## Main Goal 17 | 18 | Get a Vue options object and convert it into a svelte component 19 | 20 | Compiler Roadmap: 21 | 22 | - [x] Simple HTML elements rendering 23 | - [x] Default props values 24 | - [x] Simple properties declaration 25 | - [x] v-if statements 26 | - [x] v-else and v-else-if statements 27 | - [x] v-for statements 28 | - [ ] v-once statements 29 | - [ ] v-model support 30 | - [x] v-bind:prop / :prop support 31 | - [ ] v-bind="obj" support 32 | - [ ] Methods 33 | - [ ] Computed properties 34 | - [ ] Mixins 35 | - [ ] Lifecycle hooks 36 | - [ ] Calls to $nextTick 37 | - [ ] Filters 38 | - [ ] Calls to $options 39 | 40 | ... 41 | 42 | ## Usage 43 | 44 | ```js 45 | import Vue2Svelte from 'vue2svelte' 46 | 47 | const VueComponent = { 48 | template: `
{{ item }}
`, 49 | props: { 50 | list: { 51 | default: ['item 1', 'item 2'] 52 | } 53 | }, 54 | data () { 55 | return { 56 | hello: 'world' 57 | } 58 | } 59 | } 60 | 61 | const SvelteComponent = new Vue2Svelte(VueComponent) 62 | 63 | console.log(SvelteComponent.compile()) 64 | // Output:
{#each list as item}{item}{/each}
65 | 66 | ``` 67 | 68 | Currently it can compile simple pure components with props and data. 69 | 70 | This project is a little bit complex, so if you can help please do it! 71 | -------------------------------------------------------------------------------- /demo/src/views/Home.vue: -------------------------------------------------------------------------------- 1 | 31 | 32 | 84 | 99 | -------------------------------------------------------------------------------- /src/TemplateCompiler.ts: -------------------------------------------------------------------------------- 1 | import { ComponentOptions } from 'vue' 2 | import * as Compiler from 'vue-template-compiler' 3 | import { Vue } from 'vue/types/vue' 4 | 5 | const FromTypes = { 6 | isASTElement (T: Compiler.ASTNode): T is Compiler.ASTElement { 7 | return T.type === 1 8 | }, 9 | isASTExpression (T: Compiler.ASTNode): T is Compiler.ASTExpression { 10 | return T.type === 2 11 | }, 12 | isASTText (T: Compiler.ASTNode): T is Compiler.ASTText { 13 | return T.type === 3 14 | }, 15 | } 16 | 17 | export default class TemplateCompiler { 18 | private rawTemplate: string 19 | private compiledVueTemplate: Compiler.CompiledResult 20 | constructor (options: ComponentOptions) { 21 | this.rawTemplate = options.template 22 | // @ts-ignore 23 | this.compiledVueTemplate = Compiler.compile(this.rawTemplate, options) 24 | } 25 | public compile (ast = this.compiledVueTemplate.ast) { 26 | const children = ast.children.map((astChild) => this.compileNode(astChild)).join('') 27 | return `<${ast.tag}>${children}` 28 | } 29 | private compileAttrs (node: Compiler.ASTElement): string { 30 | if (!node.attrs) { 31 | return '' 32 | } 33 | return node.attrs.map((attr) => { 34 | // "dynamic" property is not mapped by vue-template-compiler declaration files. 35 | // maybe opening an issue is the right thing to do, but I think it's moving to ts anyway. 36 | // @ts-ignore 37 | if (attr.dynamic !== undefined) { 38 | return `${attr.name}={${attr.value}}` 39 | } 40 | return `${attr.name}=${attr.value}` 41 | }).join(' ') 42 | } 43 | private compileSurroundings (node: Compiler.ASTElement, template: string) { 44 | // Compile ifs and elses 45 | if (node.if) { 46 | const [, ...elses] = node.ifConditions 47 | const elsesTemplate = elses.map((elseItem) => { 48 | const block = this.compileNode(elseItem.block) 49 | if (elseItem.exp) { 50 | return `{:else if ${elseItem.exp}} ${block}` 51 | } 52 | return `{:else} ${block}` 53 | }).join('') 54 | template = `{#if ${node.if}}${template}${elsesTemplate}{/if}` 55 | } 56 | // Compile for 57 | if (node.for) { 58 | // I think Svelte only supports one iterator 59 | let alias = node.alias 60 | if (node.iterator1) { 61 | alias += `, ${node.iterator1}` 62 | } 63 | template = `{#each ${node.for} as ${alias}}${template}{/each}` 64 | } 65 | return template 66 | } 67 | private compileNode (node: Compiler.ASTNode) { 68 | if (FromTypes.isASTText(node)) { 69 | return node.text 70 | } 71 | if (FromTypes.isASTExpression(node)) { 72 | return node.tokens.map((token) => { 73 | if (typeof token === 'string') { 74 | return token 75 | } 76 | return `{${token['@binding']}}` 77 | }).join('') 78 | } 79 | if (FromTypes.isASTElement(node)) { 80 | const compileChildren = (): string => node.children 81 | .map((child) => this.compileNode(child)) 82 | .join('\n') 83 | 84 | const attrs = this.compileAttrs(node) 85 | 86 | const trailingAttrs = attrs ? ' ' + attrs : '' 87 | const childrenOrEmpty = node.children ? compileChildren() : '' 88 | 89 | let currentTemplate = `<${node.tag + trailingAttrs}>${childrenOrEmpty}` 90 | 91 | currentTemplate = this.compileSurroundings(node, currentTemplate) 92 | 93 | return currentTemplate 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /demo/public/img/icons/safari-pinned-tab.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | Created by potrace 1.11, written by Peter Selinger 2001-2013 9 | 10 | 12 | 148 | 149 | 150 | --------------------------------------------------------------------------------