├── .git-blame-ignore-revs
├── .github
├── renovate.json5
└── workflows
│ ├── release-continuous.yml
│ └── release.yml
├── .gitignore
├── .node-version
├── .prettierignore
├── .prettierrc
├── CHANGELOG.md
├── LICENSE
├── README.md
├── eslint.config.js
├── index-dist.html
├── index.html
├── package.json
├── pnpm-lock.yaml
├── src
├── Message.vue
├── Repl.vue
├── SplitPane.vue
├── codemirror
│ ├── CodeMirror.vue
│ ├── codemirror.css
│ └── codemirror.ts
├── core.ts
├── editor
│ ├── CodeMirrorEditor.vue
│ ├── EditorContainer.vue
│ ├── FileSelector.vue
│ ├── MonacoEditor.vue
│ └── ToggleButton.vue
├── env.d.ts
├── import-map.ts
├── index.ts
├── jsx.ts
├── monaco
│ ├── Monaco.vue
│ ├── env.ts
│ ├── highlight.ts
│ ├── language-configs.ts
│ ├── utils.ts
│ └── vue.worker.ts
├── output
│ ├── Output.vue
│ ├── Preview.vue
│ ├── PreviewProxy.ts
│ ├── Sandbox.vue
│ ├── SsrOutput.vue
│ ├── moduleCompiler.ts
│ └── srcdoc.html
├── sourcemap.ts
├── store.ts
├── template
│ ├── new-sfc.vue
│ └── welcome.vue
├── transform.ts
├── types.ts
├── utils.ts
├── vue-dev-proxy.ts
└── vue-server-renderer-dev-proxy.ts
├── ssr-stub.js
├── test
└── main.ts
├── tsconfig.json
├── vite.config.ts
└── vite.preview.config.ts
/.git-blame-ignore-revs:
--------------------------------------------------------------------------------
1 | # style: add trailing comma
2 | 497f07527b162f42123ead110031f265981f4d4d
3 |
--------------------------------------------------------------------------------
/.github/renovate.json5:
--------------------------------------------------------------------------------
1 | {
2 | $schema: 'https://docs.renovatebot.com/renovate-schema.json',
3 | extends: [
4 | 'config:base',
5 | 'schedule:weekly',
6 | 'group:allNonMajor',
7 | ':semanticCommitTypeAll(chore)',
8 | ],
9 | labels: ['dependencies'],
10 | rangeStrategy: 'bump',
11 | packageRules: [
12 | {
13 | depTypeList: ['peerDependencies'],
14 | enabled: false,
15 | },
16 | {
17 | matchPackageNames: ['codemirror'],
18 | matchUpdateTypes: ['major'],
19 | enabled: false,
20 | },
21 | ],
22 | postUpdateOptions: ['pnpmDedupe'],
23 | }
24 |
--------------------------------------------------------------------------------
/.github/workflows/release-continuous.yml:
--------------------------------------------------------------------------------
1 | name: Publish Any Commit
2 | on: [push, pull_request]
3 |
4 | jobs:
5 | build:
6 | runs-on: ubuntu-latest
7 |
8 | steps:
9 | - name: Checkout code
10 | uses: actions/checkout@v4
11 |
12 | - name: Install pnpm
13 | uses: pnpm/action-setup@v4
14 |
15 | - name: Install Node.js
16 | uses: actions/setup-node@v4
17 | with:
18 | node-version-file: '.node-version'
19 | cache: 'pnpm'
20 |
21 | - name: Install dependencies
22 | run: pnpm install
23 |
24 | - name: Build
25 | run: pnpm build
26 |
27 | - run: pnpx pkg-pr-new publish --compact
28 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Release
2 |
3 | on:
4 | push:
5 | tags:
6 | - 'v*'
7 |
8 | jobs:
9 | release:
10 | runs-on: ubuntu-latest
11 | permissions:
12 | contents: write
13 | id-token: write
14 |
15 | steps:
16 | - uses: actions/checkout@v4
17 | with:
18 | fetch-depth: 0
19 |
20 | - name: Install pnpm
21 | uses: pnpm/action-setup@v4
22 |
23 | - name: Install Node.js
24 | uses: actions/setup-node@v4
25 | with:
26 | node-version-file: '.node-version'
27 | cache: pnpm
28 | registry-url: 'https://registry.npmjs.org'
29 |
30 | - run: npx changelogithub
31 | continue-on-error: true
32 | env:
33 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
34 |
35 | - name: Install Dependencies
36 | run: pnpm i
37 |
38 | - name: Publish to NPM
39 | run: pnpm -r publish --access public --no-git-checks
40 | env:
41 | NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
42 | NPM_CONFIG_PROVENANCE: true
43 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .DS_Store
3 | dist
4 | TODOs.md
5 |
6 | # jetbrains files
7 | .idea
8 |
--------------------------------------------------------------------------------
/.node-version:
--------------------------------------------------------------------------------
1 | lts/*
2 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | dist
2 | CHANGELOG.md
3 | pnpm-lock.yaml
4 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "semi": false,
3 | "singleQuote": true
4 | }
5 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## [4.6.1](https://github.com/vuejs/repl/compare/v4.6.0...v4.6.1) (2025-06-13)
2 |
3 |
4 | ### Bug Fixes
5 |
6 | * the line number offset in the DEV ([#344](https://github.com/vuejs/repl/issues/344)) ([cc292d3](https://github.com/vuejs/repl/commit/cc292d3dba25ac872edf147e14b6c9bbf6a0c577))
7 |
8 |
9 |
10 | # [4.6.0](https://github.com/vuejs/repl/compare/v4.5.1...v4.6.0) (2025-06-13)
11 |
12 |
13 | ### Features
14 |
15 | * add support for viewing sourcemap ([#341](https://github.com/vuejs/repl/issues/341)) ([5714d5b](https://github.com/vuejs/repl/commit/5714d5b706d7c945ee5393bef545dcf70c189db3))
16 | * show SSR output ([#343](https://github.com/vuejs/repl/issues/343)) ([52a193a](https://github.com/vuejs/repl/commit/52a193a8a658d0059ee8c30345ca10ff52af7c04))
17 |
18 |
19 |
20 | ## [4.5.1](https://github.com/vuejs/repl/compare/v4.5.0...v4.5.1) (2025-02-19)
21 |
22 |
23 | ### Bug Fixes
24 |
25 | * apply builtin import map after deserialize ([#315](https://github.com/vuejs/repl/issues/315)) ([e62ddda](https://github.com/vuejs/repl/commit/e62ddda06fe3339a467f88b13b0271c2a5c7e96d))
26 | * **split-pane:** rendering order comes from the store.show-output ([0bd4c17](https://github.com/vuejs/repl/commit/0bd4c17b6dd26d4e17387f50e089dd3ffefaf054))
27 | * ts error ([a927083](https://github.com/vuejs/repl/commit/a927083734f1d4dae5be0d200aafd26e49ce82aa))
28 |
29 |
30 | ### Features
31 |
32 | * add core entry for node usage ([#310](https://github.com/vuejs/repl/issues/310)) ([da105a4](https://github.com/vuejs/repl/commit/da105a42618899d214701a5fb6549719f73331bb))
33 | * **editor:** scrollbar style for firefox ([#320](https://github.com/vuejs/repl/issues/320)) ([bbc740b](https://github.com/vuejs/repl/commit/bbc740bfa840dfb6c77824470f1ffdc4a6261e85))
34 | * export `Sandbox` as standalone output component ([#309](https://github.com/vuejs/repl/issues/309)) ([b549715](https://github.com/vuejs/repl/commit/b5497152fefe8f190eca59755bedb27b2f3178f2))
35 | * **store:** return `setImportMap` and add `merge` parameter ([9f53bd1](https://github.com/vuejs/repl/commit/9f53bd11aee1d75984e5597878e53bec4ae168e5))
36 | * support cache selected typescript version ([#305](https://github.com/vuejs/repl/issues/305)) ([33ca3c0](https://github.com/vuejs/repl/commit/33ca3c0317aa0418c094ec8f9e6712d81fa11465))
37 | * support vapor template-only component ([#322](https://github.com/vuejs/repl/issues/322)) ([9ae056b](https://github.com/vuejs/repl/commit/9ae056b701ff54446c5c1ec9f29444d5239e0931))
38 |
39 |
40 |
41 | # [4.5.0](https://github.com/vuejs/repl/compare/v4.4.3...v4.5.0) (2025-02-03)
42 |
43 |
44 | ### Features
45 |
46 | * pass on descriptor vapor flag when compiling template ([adaaceb](https://github.com/vuejs/repl/commit/adaaceb24984435ae02ab3eda071f10dba9e0362))
47 |
48 |
49 |
50 | ## [4.4.3](https://github.com/vuejs/repl/compare/v4.4.2...v4.4.3) (2025-01-02)
51 |
52 |
53 | ### Bug Fixes
54 |
55 | * transform jsx for entire file ([48325f9](https://github.com/vuejs/repl/commit/48325f99e010c3065c99efd4fb3e95950cda9596)), closes [#301](https://github.com/vuejs/repl/issues/301) [#300](https://github.com/vuejs/repl/issues/300)
56 |
57 |
58 |
59 | ## [4.4.2](https://github.com/vuejs/repl/compare/v4.4.1...v4.4.2) (2024-09-16)
60 |
61 |
62 | ### Bug Fixes
63 |
64 | * output toggle button ([#279](https://github.com/vuejs/repl/issues/279)) ([93051f3](https://github.com/vuejs/repl/commit/93051f35a232b53d01dd0a40623cab5b11baa3ee))
65 | * upgrade vue language tools ([ec393cf](https://github.com/vuejs/repl/commit/ec393cfe4b8b8008976e4fd2017bd112d98fa599))
66 |
67 |
68 |
69 | ## [4.4.1](https://github.com/vuejs/repl/compare/v4.4.0...v4.4.1) (2024-09-08)
70 |
71 |
72 | ### Bug Fixes
73 |
74 | * cancel creating new file ([#281](https://github.com/vuejs/repl/issues/281)) ([7467f38](https://github.com/vuejs/repl/commit/7467f38f65e4b05dacc389644a8001b24f86fcdb))
75 | * type error ([6653d0e](https://github.com/vuejs/repl/commit/6653d0e4b0b30eeee4bfe0c0c92bf00a84c0c753))
76 |
77 |
78 | ### Features
79 |
80 | * add `autoSave` toggle button ([#283](https://github.com/vuejs/repl/issues/283)) ([83d8e48](https://github.com/vuejs/repl/commit/83d8e487de724261cf709c5648cc2512b4c33732))
81 | * export `languageToolsVersion` ([5a92a92](https://github.com/vuejs/repl/commit/5a92a9259da01da4aa30b09ed6dcedfb4503c71d))
82 |
83 |
84 |
85 | # [4.4.0](https://github.com/vuejs/repl/compare/v4.3.1...v4.4.0) (2024-09-07)
86 |
87 |
88 | ### Bug Fixes
89 |
90 | * debounce reloadLanguageTools in monaco ([f9f650a](https://github.com/vuejs/repl/commit/f9f650ada945f7ea597b7e7b51c132c4594bd5cb)), closes [#275](https://github.com/vuejs/repl/issues/275) [#263](https://github.com/vuejs/repl/issues/263)
91 | * no default value for object props ([d786626](https://github.com/vuejs/repl/commit/d78662652ec18f4610facc8f3e2a50f38c01f93f))
92 | * rollback & pin volar version ([c6f58c7](https://github.com/vuejs/repl/commit/c6f58c7f4d04799a64f788251362f8349728bea4))
93 |
94 |
95 | ### Features
96 |
97 | * add more customization options ([#274](https://github.com/vuejs/repl/issues/274)) ([c73b786](https://github.com/vuejs/repl/commit/c73b7868d73d3dad792c80a36507ce92234443d4))
98 | * expose `editorOptions.monacoOptions` ([00176d0](https://github.com/vuejs/repl/commit/00176d007ff5eb74e216abff3de87e625cde543b)), closes [#277](https://github.com/vuejs/repl/issues/277) [#232](https://github.com/vuejs/repl/issues/232)
99 | * reactivity `autoSave` option ([#266](https://github.com/vuejs/repl/issues/266)) ([d90082a](https://github.com/vuejs/repl/commit/d90082a2e44956e08fc64d296661b30ea4590506))
100 |
101 |
102 |
103 | ## [4.3.1](https://github.com/vuejs/repl/compare/v4.3.0...v4.3.1) (2024-07-03)
104 |
105 |
106 | ### Bug Fixes
107 |
108 | * match source file ([4cf06b6](https://github.com/vuejs/repl/commit/4cf06b63fa1807ccfbf14c0ef1f1fa9b1f268717))
109 |
110 |
111 | ### Reverts
112 |
113 | * refactor: replace assert with assert-plus ([e55baa4](https://github.com/vuejs/repl/commit/e55baa481f70c387f24cebfdfa7f143814fb0ce2))
114 |
115 |
116 |
117 | # [4.3.0](https://github.com/vuejs/repl/compare/v4.2.1...v4.3.0) (2024-07-02)
118 |
119 |
120 | ### Bug Fixes
121 |
122 | * alert if deserialization fails ([071b1d1](https://github.com/vuejs/repl/commit/071b1d1216fa3df2e14c9c6e5453cfe85eed4b79)), closes [#256](https://github.com/vuejs/repl/issues/256)
123 | * move assert-plus to devDep ([dd9f1bb](https://github.com/vuejs/repl/commit/dd9f1bb74c17f19c25aa9b0e485366781094e818))
124 |
125 |
126 | ### Features
127 |
128 | * show view size while dragging split pane ([#253](https://github.com/vuejs/repl/issues/253)) ([a6bbeea](https://github.com/vuejs/repl/commit/a6bbeea7b8ec7c1302ba08afa0c789ad198cc8e2))
129 | * volar 2.x ([#225](https://github.com/vuejs/repl/issues/225)) ([47030b6](https://github.com/vuejs/repl/commit/47030b66a6f1811a24d8292f9f3aa5185f7e8e23))
130 |
131 |
132 |
133 | ## [4.2.1](https://github.com/vuejs/repl/compare/v4.2.0...v4.2.1) (2024-05-31)
134 |
135 |
136 | ### Bug Fixes
137 |
138 | * don't overwrite import map from initial state ([1410b8c](https://github.com/vuejs/repl/commit/1410b8cac4dd993c5ba6a94e299b261ed84c3f12)), closes [#252](https://github.com/vuejs/repl/issues/252)
139 |
140 |
141 |
142 | # [4.2.0](https://github.com/vuejs/repl/compare/v4.1.2...v4.2.0) (2024-05-26)
143 |
144 |
145 | ### Bug Fixes
146 |
147 | * refine dragging view area ([#246](https://github.com/vuejs/repl/issues/246)) ([df14639](https://github.com/vuejs/repl/commit/df14639b85edc0e153ba2fd6f29656785a0af3aa))
148 | * specify unspported pre-processors lang ([#212](https://github.com/vuejs/repl/issues/212)) ([5cea974](https://github.com/vuejs/repl/commit/5cea974451ae23b82bea0c6270ad7ac726d831a4))
149 |
150 |
151 | ### Features
152 |
153 | * `CodeMirror` support `autoSave` option ([#249](https://github.com/vuejs/repl/issues/249)) ([ae80c5b](https://github.com/vuejs/repl/commit/ae80c5b995ffa375013b665fd9a212c5607a1236))
154 | * add `autoSave` option ([#247](https://github.com/vuejs/repl/issues/247)) ([d47eca5](https://github.com/vuejs/repl/commit/d47eca5926dcac798171fc216fcee2e21f275dd4))
155 | * jsx for vue ([#248](https://github.com/vuejs/repl/issues/248)) ([d5b0d40](https://github.com/vuejs/repl/commit/d5b0d40ecc7f630b89e45ebe8472bc4e7563b3e2))
156 |
157 |
158 | ### Performance Improvements
159 |
160 | * avoid parse repeatedly ([c6b7352](https://github.com/vuejs/repl/commit/c6b735298d5ab630cdc130aad7b8acaf7c9c41bb))
161 |
162 |
163 |
164 | ## [4.1.2](https://github.com/vuejs/repl/compare/v4.1.1...v4.1.2) (2024-04-26)
165 |
166 |
167 | ### Bug Fixes
168 |
169 | * dynamic import ([#213](https://github.com/vuejs/repl/issues/213)) ([bb6f1fe](https://github.com/vuejs/repl/commit/bb6f1fe8599f1a11cd1b8aa40a630bea09d2b577))
170 | * fix file rename breaking ([caace63](https://github.com/vuejs/repl/commit/caace639bde964323a253bdcd252a18869973a1d))
171 |
172 |
173 |
174 | ## [4.1.1](https://github.com/vuejs/repl/compare/v4.1.0...v4.1.1) (2024-02-14)
175 |
176 |
177 | ### Bug Fixes
178 |
179 | * add vue import maps for default import map ([c74673f](https://github.com/vuejs/repl/commit/c74673fb55d2232de55562e62a818142681bdc8b))
180 | * reload preview style error after switching theme ([#214](https://github.com/vuejs/repl/issues/214)) ([bc4c76c](https://github.com/vuejs/repl/commit/bc4c76c3f143b5edc3546d80002cb813704b8351))
181 |
182 |
183 |
184 | # [4.1.0](https://github.com/vuejs/repl/compare/v4.0.2...v4.1.0) (2024-02-11)
185 |
186 |
187 | ### Features
188 |
189 | * add `previewTheme` prop ([c830fc4](https://github.com/vuejs/repl/commit/c830fc434a1781523af332d289957bc485f51a0b))
190 |
191 |
192 |
193 | ## [4.0.2](https://github.com/vuejs/repl/compare/v4.0.1...v4.0.2) (2024-02-10)
194 |
195 |
196 | ### Bug Fixes
197 |
198 | * respect vue version at initialization ([ef22052](https://github.com/vuejs/repl/commit/ef22052055590dbbe6e85e26ce368938b0c93266))
199 |
200 |
201 |
202 | ## [4.0.1](https://github.com/vuejs/repl/compare/v4.0.0...v4.0.1) (2024-02-10)
203 |
204 |
205 | ### Bug Fixes
206 |
207 | * save version only when serialize ([d3ee13d](https://github.com/vuejs/repl/commit/d3ee13ded3c5a162bf990ef83cb9a38991792170))
208 |
209 |
210 | ### Features
211 |
212 | * register language configuration ([3ad7035](https://github.com/vuejs/repl/commit/3ad7035e26cb02626b06e58f53e12ffb5443a5fc))
213 |
214 |
215 |
216 | # [4.0.0](https://github.com/vuejs/repl/compare/v4.0.0-beta.0...v4.0.0) (2024-02-10)
217 |
218 |
219 | ### Features
220 |
221 | * expose loading status ([eee6bb3](https://github.com/vuejs/repl/commit/eee6bb38ddecbe8bf7ba3ab77d6a549e654b6313))
222 | * save vue version ([08b4492](https://github.com/vuejs/repl/commit/08b4492fe883bdd4bbe7fd972cd3fbbd8f6416cf))
223 |
224 |
225 |
226 | # [4.0.0-beta.0](https://github.com/vuejs/repl/compare/v4.0.0-alpha.1...v4.0.0-beta.0) (2024-02-10)
227 |
228 |
229 | ### Bug Fixes
230 |
231 | * pass readonly in code mirror editor ([1100158](https://github.com/vuejs/repl/commit/1100158aec97dae9cf47ac04ff2bb9ec00d05e58))
232 |
233 |
234 |
235 | # [4.0.0-alpha.1](https://github.com/vuejs/repl/compare/v4.0.0-alpha.0...v4.0.0-alpha.1) (2024-01-24)
236 |
237 |
238 | ### Bug Fixes
239 |
240 | * don't re-create import map file ([9e6f078](https://github.com/vuejs/repl/commit/9e6f078206883821d9bc618a194cf50333f38d3d))
241 |
242 |
243 |
244 | # [4.0.0-alpha.0](https://github.com/vuejs/repl/compare/v3.3.0...v4.0.0-alpha.0) (2024-01-21)
245 |
246 |
247 | ### Bug Fixes
248 |
249 | * add corresponding black theme background ([#206](https://github.com/vuejs/repl/issues/206)) ([3921c85](https://github.com/vuejs/repl/commit/3921c85f90a40a871838f9740fa3588e2cfa4758))
250 | * don't overwrite `a` tag without href ([#209](https://github.com/vuejs/repl/issues/209)) ([c7fcf38](https://github.com/vuejs/repl/commit/c7fcf381f195ffce6284cc92c26f8a90e09b484a))
251 | * don't show tsconfig if not present ([ca548b2](https://github.com/vuejs/repl/commit/ca548b240addd4f58851a545622684a00eb09a0a))
252 |
253 |
254 | ### Features
255 |
256 | * export `package.json` ([79a22de](https://github.com/vuejs/repl/commit/79a22deb84aa50e31cf6506a67e93f69291fb82f))
257 |
258 |
259 |
260 | # [3.3.0](https://github.com/vuejs/repl/compare/v3.2.0...v3.3.0) (2024-01-11)
261 |
262 |
263 | ### Bug Fixes
264 |
265 | * default to white color on dark theme ([#202](https://github.com/vuejs/repl/issues/202)) ([481035a](https://github.com/vuejs/repl/commit/481035a443031e50de26b75c8e5b86fbb8ca96f2))
266 | * serialize import maps ([e085e30](https://github.com/vuejs/repl/commit/e085e3041a228fe0ec076056e23e8f55258120ab)), closes [#204](https://github.com/vuejs/repl/issues/204)
267 |
268 |
269 | ### Features
270 |
271 | * add theme as classname to sandbox ([#203](https://github.com/vuejs/repl/issues/203)) ([7e9dc0f](https://github.com/vuejs/repl/commit/7e9dc0f3b1f2c488664ccfa22cdf21ba19926158))
272 | * apply theme to preview ([#200](https://github.com/vuejs/repl/issues/200)) ([7ae1061](https://github.com/vuejs/repl/commit/7ae106129274f13393808000fd25995d919ae0bd))
273 | * mutable sfc options ([9e83b09](https://github.com/vuejs/repl/commit/9e83b09344ecc90ad0e28024a9b260fff97ffccd))
274 | * support custom template ([#196](https://github.com/vuejs/repl/issues/196)) ([8038b49](https://github.com/vuejs/repl/commit/8038b49cc5fb76a7dc34acffcda5b3f55ff8aa11))
275 |
276 |
277 |
278 | # [3.2.0](https://github.com/vuejs/repl/compare/v3.1.1...v3.2.0) (2024-01-03)
279 |
280 |
281 | ### Bug Fixes
282 |
283 | * **codemirror:** fix codemirror editor showing nothing on start on small layouts when starting in ouput mode ([#181](https://github.com/vuejs/repl/issues/181)) ([6d7598d](https://github.com/vuejs/repl/commit/6d7598d763c79d777efae4e17ef61132930ae9a0))
284 | * **messages:** place error messages in editor in front of bottom toggles ([#183](https://github.com/vuejs/repl/issues/183)) ([b1594d0](https://github.com/vuejs/repl/commit/b1594d07dbb29d7d3c15afa4110a4005d4245297))
285 | * **Preview:** fix style loading delay ([#191](https://github.com/vuejs/repl/issues/191)) ([ece4414](https://github.com/vuejs/repl/commit/ece4414186fab8bb19290ed047e2a4ab665ae3ef))
286 |
287 |
288 | ### Features
289 |
290 | * **playground:** use a height of 100dvh with fallback to original 100vh ([#182](https://github.com/vuejs/repl/issues/182)) ([9e99990](https://github.com/vuejs/repl/commit/9e99990f7aa2bd8792510fcc03fda931691e8353))
291 |
292 |
293 |
294 | ## [3.1.1](https://github.com/vuejs/repl/compare/v3.1.0...v3.1.1) (2024-01-02)
295 |
296 |
297 | ### Bug Fixes
298 |
299 | * pass sfc template options to sfc parse ([d72dfdf](https://github.com/vuejs/repl/commit/d72dfdfd2e2670592c957616fcf4e694609912a0))
300 |
301 |
302 |
303 | # [3.1.0](https://github.com/vuejs/repl/compare/v3.0.1...v3.1.0) (2023-12-21)
304 |
305 |
306 | ### Bug Fixes
307 |
308 | * remove onigasm dep ([e7a73ac](https://github.com/vuejs/repl/commit/e7a73ac249ce44a6f4b661f6e6ff4842f3225d6b))
309 |
310 |
311 | ### Features
312 |
313 | * use shikiji for more accurate highlight ([#190](https://github.com/vuejs/repl/issues/190)) ([e79aa1a](https://github.com/vuejs/repl/commit/e79aa1af8dc898d9170c5f33ee031ead61f32320))
314 |
315 |
316 |
317 | ## [3.0.1](https://github.com/vuejs/repl/compare/v3.0.0...v3.0.1) (2023-12-19)
318 |
319 |
320 | ### Bug Fixes
321 |
322 | * ensure reuse AST in non-inline mode ([5e4c710](https://github.com/vuejs/repl/commit/5e4c7101e4b6cc27fb0810390b0ca0287a101149))
323 |
324 |
325 |
326 | # [3.0.0](https://github.com/vuejs/repl/compare/v2.9.0...v3.0.0) (2023-11-30)
327 |
328 |
329 | ### Bug Fixes
330 |
331 | * handle main file src prefix when setting files + avoid infinite loop due to state.error push ([743b731](https://github.com/vuejs/repl/commit/743b73121dbd63f164a013c8ba722d0a8bfe5ebd))
332 | * make main repl styles lower specificity for easier override ([fbfaa44](https://github.com/vuejs/repl/commit/fbfaa4495c9bbf3ab936bec27445c52c9521b67e))
333 |
334 |
335 |
336 | # [2.9.0](https://github.com/vuejs/repl/compare/v2.8.1...v2.9.0) (2023-11-30)
337 |
338 |
339 | ### Bug Fixes
340 |
341 | * **types:** fix editor prop types ([828f202](https://github.com/vuejs/repl/commit/828f2027ff3986a029de3833f521525c7ac3e1d7))
342 |
343 |
344 | ### Features
345 |
346 | * support custom element styles ([#173](https://github.com/vuejs/repl/issues/173)) ([812730d](https://github.com/vuejs/repl/commit/812730db62b6f1865cee90b67f9f593412a0dce6))
347 |
348 |
349 |
350 | ## [2.8.1](https://github.com/vuejs/repl/compare/v2.8.0...v2.8.1) (2023-11-28)
351 |
352 |
353 | ### Bug Fixes
354 |
355 | * new sfc file template ([79643d7](https://github.com/vuejs/repl/commit/79643d71a5eabd7e7c9c092e8501cc07f9ee5136))
356 | * worker plugins for vite 5 ([6e66250](https://github.com/vuejs/repl/commit/6e6625084d0c9ba8c24915ebd1060b7421e0de5b))
357 |
358 |
359 |
360 | # [2.8.0](https://github.com/vuejs/repl/compare/v2.7.0...v2.8.0) (2023-11-19)
361 |
362 |
363 | ### Features
364 |
365 | * add template for new file ([9a0be1d](https://github.com/vuejs/repl/commit/9a0be1df8c06ffdeab2985f9e9cd5f2cde1437fe))
366 |
367 |
368 |
369 | # [2.7.0](https://github.com/vuejs/repl/compare/v2.6.3...v2.7.0) (2023-11-12)
370 |
371 |
372 | ### Features
373 |
374 | * support toggling between dev/prod for Vue runtime ([8d3a2e6](https://github.com/vuejs/repl/commit/8d3a2e62358104663af48531467ac8eda4bafffa))
375 |
376 |
377 |
378 | ## [2.6.3](https://github.com/vuejs/repl/compare/v2.6.2...v2.6.3) (2023-11-03)
379 |
380 |
381 |
382 | ## [2.6.2](https://github.com/vuejs/repl/compare/v2.6.1...v2.6.2) (2023-11-01)
383 |
384 |
385 |
386 | ## [2.6.1](https://github.com/vuejs/repl/compare/v2.6.0...v2.6.1) (2023-10-26)
387 |
388 |
389 |
390 | # [2.6.0](https://github.com/vuejs/repl/compare/v2.5.8...v2.6.0) (2023-10-26)
391 |
392 |
393 | ### Features
394 |
395 | * add layout reverse api [#162](https://github.com/vuejs/repl/issues/162) ([#163](https://github.com/vuejs/repl/issues/163)) ([c1cd77a](https://github.com/vuejs/repl/commit/c1cd77a913b050e2fb3d921d4dcd86a1db74b8b1))
396 | * support custom display placeholder content ([#160](https://github.com/vuejs/repl/issues/160)) ([9ca27a1](https://github.com/vuejs/repl/commit/9ca27a12cf92b6ac6b7132a5c2ae667a13af4faa))
397 |
398 |
399 |
400 | ## [2.5.8](https://github.com/vuejs/repl/compare/v2.5.7...v2.5.8) (2023-08-10)
401 |
402 |
403 | ### Bug Fixes
404 |
405 | * don't set editor value if not changed ([bd59eef](https://github.com/vuejs/repl/commit/bd59eefb1d2731179f772ab118ee642f453fa5d2)), closes [#147](https://github.com/vuejs/repl/issues/147)
406 |
407 |
408 |
409 | ## [2.5.7](https://github.com/vuejs/repl/compare/v2.5.6...v2.5.7) (2023-08-08)
410 |
411 |
412 | ### Bug Fixes
413 |
414 | * respect value from monaco editor props ([49fdc71](https://github.com/vuejs/repl/commit/49fdc7161ec91fed617043aca0b751858a10289e)), closes [#145](https://github.com/vuejs/repl/issues/145)
415 |
416 |
417 |
418 | ## [2.5.6](https://github.com/vuejs/repl/compare/v2.5.5...v2.5.6) (2023-07-31)
419 |
420 |
421 | ### Bug Fixes
422 |
423 | * remove preinstall ([8e41043](https://github.com/vuejs/repl/commit/8e410433eb46b45845c39aca8ad2895c3fabae12)), closes [/github.com/vuejs/repl/commit/569fe6275db45a420850cac9419b4614a51a360e#r123111912](https://github.com//github.com/vuejs/repl/commit/569fe6275db45a420850cac9419b4614a51a360e/issues/r123111912)
424 |
425 |
426 |
427 | ## [2.5.5](https://github.com/vuejs/repl/compare/v2.5.4...v2.5.5) (2023-07-09)
428 |
429 |
430 | ### Features
431 |
432 | * expose dependency version ([aecfd8a](https://github.com/vuejs/repl/commit/aecfd8a92e6e7814dd6dbd5d5e94f71ef9fe5b1a))
433 |
434 |
435 |
436 | ## [2.5.4](https://github.com/vuejs/repl/compare/v2.5.3...v2.5.4) (2023-07-09)
437 |
438 |
439 | ### Bug Fixes
440 |
441 | * replace NODE_ENV ([863f8f3](https://github.com/vuejs/repl/commit/863f8f39d36d25240388a9c5bc68eff0ea7e7856))
442 |
443 |
444 |
445 | ## [2.5.3](https://github.com/vuejs/repl/compare/v2.5.2...v2.5.3) (2023-07-08)
446 |
447 |
448 | ### Bug Fixes
449 |
450 | * filename index auto-increment ([#133](https://github.com/vuejs/repl/issues/133)) ([4f55810](https://github.com/vuejs/repl/commit/4f55810f729fc61e22eafa7ea69afe79bcfe1cb6))
451 | * make reloadLanguageTools optional ([5ab1a2d](https://github.com/vuejs/repl/commit/5ab1a2d149820ecb737c3bc97581a87f3adc83d7))
452 | * use dev version of compiler ([#132](https://github.com/vuejs/repl/issues/132)) ([bfc3522](https://github.com/vuejs/repl/commit/bfc3522422926b0e3f18c1368111066cf268e206))
453 |
454 |
455 |
456 | ## [2.5.2](https://github.com/vuejs/repl/compare/v2.5.1...v2.5.2) (2023-07-06)
457 |
458 |
459 | ### Bug Fixes
460 |
461 | * remove postinstall ([8167272](https://github.com/vuejs/repl/commit/816727232d0adac0c0955c1d6bee9d7be7f70d61))
462 |
463 |
464 |
465 | ## [2.5.1](https://github.com/vuejs/repl/compare/v2.5.0...v2.5.1) (2023-07-05)
466 |
467 |
468 | ### Bug Fixes
469 |
470 | * cannot get ts module in prod env ([0cc220d](https://github.com/vuejs/repl/commit/0cc220d7efaafaaa3b8af07c34fd27c7825caa8b))
471 | * cdn file models were accidentally disposed ([4301d86](https://github.com/vuejs/repl/commit/4301d8659ad84d9c6b66d63f1567cf31119eb9f4))
472 |
473 |
474 | ### Features
475 |
476 | * use ts version option for ts lib dts acquire ([376fe3b](https://github.com/vuejs/repl/commit/376fe3ba2582fc128ccc4bbb2cd4b19666a5f1ec))
477 |
478 |
479 |
480 | # [2.5.0](https://github.com/vuejs/repl/compare/v2.4.0...v2.5.0) (2023-07-05)
481 |
482 |
483 | ### Bug Fixes
484 |
485 | * change message toggle position ([#120](https://github.com/vuejs/repl/issues/120)) ([3f7e090](https://github.com/vuejs/repl/commit/3f7e090c143ca0a40b0bff1d13f2db3e6964b17a))
486 |
487 |
488 | ### Features
489 |
490 | * download TS dynamically ([#125](https://github.com/vuejs/repl/issues/125)) ([97f698f](https://github.com/vuejs/repl/commit/97f698f1f88690ab371e156ac2113955fdaa5fa8))
491 | * expose TS localized languages ([a52dd14](https://github.com/vuejs/repl/commit/a52dd1468a6d6fb8ce4927a0a6771529f6a0f1ab))
492 | * respect browser language ([f9fedcd](https://github.com/vuejs/repl/commit/f9fedcd1ca56a6965c9617a16d2fba834f8a44a1)), closes [#123](https://github.com/vuejs/repl/issues/123)
493 | * upgrade volar ([d925ba3](https://github.com/vuejs/repl/commit/d925ba3c8a08966eab06eaf2720a7476415e7760))
494 |
495 |
496 |
497 | # [2.4.0](https://github.com/vuejs/repl/compare/v2.3.0...v2.4.0) (2023-06-28)
498 |
499 |
500 | ### Bug Fixes
501 |
502 | * multiple style tags ([#116](https://github.com/vuejs/repl/issues/116)) ([f0f5512](https://github.com/vuejs/repl/commit/f0f5512f49832321a6c96631025927635a834d9a))
503 | * strip src prefix on dialog ([d29d1de](https://github.com/vuejs/repl/commit/d29d1de3f31930005dfc0b29f7d8a0435c0f94a4))
504 |
505 |
506 | ### Features
507 |
508 | * add `reload` function ([#103](https://github.com/vuejs/repl/issues/103)) ([12ebcea](https://github.com/vuejs/repl/commit/12ebceab49c2a56702fffdfc01bb8b0bc3a708ca))
509 | * add monaco light theme ([#121](https://github.com/vuejs/repl/issues/121)) ([ead9667](https://github.com/vuejs/repl/commit/ead9667a85c1f217dab5955ebd9a11992b3fbe65))
510 | * memorize show error state ([#117](https://github.com/vuejs/repl/issues/117)) ([ab4b7cd](https://github.com/vuejs/repl/commit/ab4b7cd4d2c99b2750e29feaa0b3487f4bb8ed85))
511 |
512 |
513 |
514 | # [2.3.0](https://github.com/vuejs/repl/compare/v2.2.0...v2.3.0) (2023-06-24)
515 |
516 |
517 | ### Features
518 |
519 | * add tsconfig file ([#114](https://github.com/vuejs/repl/issues/114)) ([29f6af5](https://github.com/vuejs/repl/commit/29f6af5037826a6d37f77ba4cae748e7297152e3))
520 |
521 |
522 |
523 | # [2.2.0](https://github.com/vuejs/repl/compare/v2.1.4...v2.2.0) (2023-06-24)
524 |
525 |
526 | ### Bug Fixes
527 |
528 | * don't dispose in-memory files ([5f543da](https://github.com/vuejs/repl/commit/5f543da6815e30c76dc3a595b993a380043af54b))
529 | * set page height ([ee814e7](https://github.com/vuejs/repl/commit/ee814e7313162f19d45dcff7a3ecabedfdf081d5)), closes [#112](https://github.com/vuejs/repl/issues/112)
530 |
531 |
532 | ### Features
533 |
534 | * add default height for Repl component ([#109](https://github.com/vuejs/repl/issues/109)) ([d9673eb](https://github.com/vuejs/repl/commit/d9673eb4c7a3e20ca7d0f1e152d177c6c1f8956d))
535 | * add error toggle ([#98](https://github.com/vuejs/repl/issues/98)) ([51819cc](https://github.com/vuejs/repl/commit/51819ccd3adcd40c189bd216f635ca6f62c4bc56))
536 |
537 |
538 |
539 | ## [2.1.4](https://github.com/vuejs/repl/compare/v2.1.3...v2.1.4) (2023-06-23)
540 |
541 |
542 | ### Bug Fixes
543 |
544 | * default main file path ([c0184da](https://github.com/vuejs/repl/commit/c0184da073456706c44cc5e78e2d3283f4d3fe0f))
545 | * **monaco:** enable `fixedOverflowWidgets` option ([#110](https://github.com/vuejs/repl/issues/110)) ([c7ddf12](https://github.com/vuejs/repl/commit/c7ddf12f25b23675c12c2760297c7d7d37668943))
546 |
547 |
548 |
549 | ## [2.1.3](https://github.com/vuejs/repl/compare/v2.1.2...v2.1.3) (2023-06-22)
550 |
551 |
552 | ### Bug Fixes
553 |
554 | * actually fix editor type lol ([95ab2ab](https://github.com/vuejs/repl/commit/95ab2abc29b01a565ba7bc25ef293f1434db5ef6))
555 |
556 |
557 |
558 | ## [2.1.2](https://github.com/vuejs/repl/compare/v2.1.1...v2.1.2) (2023-06-22)
559 |
560 |
561 | ### Bug Fixes
562 |
563 | * ensure imported editor can be passed as prop without type error ([414b0e6](https://github.com/vuejs/repl/commit/414b0e6cb729234ccb188332b22c184e44f162e0))
564 |
565 |
566 |
567 | ## [2.1.1](https://github.com/vuejs/repl/compare/v2.1.0...v2.1.1) (2023-06-22)
568 |
569 |
570 | ### Bug Fixes
571 |
572 | * **types:** fix editor generated dts ([e5705df](https://github.com/vuejs/repl/commit/e5705df7d1ea4a44d9f6eba4443e28712631053a))
573 |
574 |
575 |
576 | # [2.1.0](https://github.com/vuejs/repl/compare/v2.0.0...v2.1.0) (2023-06-22)
577 |
578 |
579 | ### Features
580 |
581 | * support custom file go to difinition ([#102](https://github.com/vuejs/repl/issues/102)) ([519b0cc](https://github.com/vuejs/repl/commit/519b0cc079dccdb08ed00f1b5d2fb0c965fbab03))
582 |
583 |
584 |
585 | # [2.0.0](https://github.com/vuejs/repl/compare/v1.5.0...v2.0.0) (2023-06-22)
586 |
587 |
588 | ### Bug Fixes
589 |
590 | * disable pug and script setup codeLens ([1c6e646](https://github.com/vuejs/repl/commit/1c6e6464bea009b279fe43ed401e722230bf95bd))
591 | * don't delete dts models ([d04a1ed](https://github.com/vuejs/repl/commit/d04a1ed01a9f5aaeafc6845d6165dcc9b45b7a04))
592 | * f@ck ([0aa0a24](https://github.com/vuejs/repl/commit/0aa0a24e9c196d846827623b48b5eace0b8498a0))
593 | * larger font size ([#100](https://github.com/vuejs/repl/issues/100)) ([a1a3fe1](https://github.com/vuejs/repl/commit/a1a3fe1294cf0daa89e30902d607addbdd525b6e))
594 | * make monaco editor works normally ([f538199](https://github.com/vuejs/repl/commit/f538199e5cd99c380b3b5468cc660d47a6910783))
595 | * minor fix ([aacba06](https://github.com/vuejs/repl/commit/aacba0673414b759377e3cbfa764bff82d64f857))
596 | * split monaco out ([fd0b06a](https://github.com/vuejs/repl/commit/fd0b06af78e25632ee4fd6525ae5c90ef2c51f0b))
597 | * styles ([6aee9cf](https://github.com/vuejs/repl/commit/6aee9cfa7df19de0f60f0d9fe2ccbd2291998cbb))
598 | * update exports and types ([e4988b0](https://github.com/vuejs/repl/commit/e4988b0a9ddbc9888e481fd8f1c000b1756a5ba5))
599 | * use monaco-volar ([9846c8e](https://github.com/vuejs/repl/commit/9846c8e67720c2d7402d52bc1c2106a9a1b28c08))
600 | * use worker ([e1e80b9](https://github.com/vuejs/repl/commit/e1e80b9e40805bd541ccd80d48b01228378bf2c7))
601 |
602 |
603 | ### Features
604 |
605 | * add ls ([a7bffd6](https://github.com/vuejs/repl/commit/a7bffd64c43e8ff375f1e59b62c78ca13969c723))
606 | * complete provideDefinition ([2035fc4](https://github.com/vuejs/repl/commit/2035fc49977e5c2f5e8c57fe80c0bb53bb85df70))
607 | * completed provideCompletionItems ([81ef510](https://github.com/vuejs/repl/commit/81ef51028f9188fcf598e78e45836f09b9f191ae))
608 | * completed provideHover ([3922239](https://github.com/vuejs/repl/commit/39222398316adfda107f2b00d2cc27523f6cef52))
609 | * completed provideSignatureHelp ([c599f2b](https://github.com/vuejs/repl/commit/c599f2bdf136d0d02e0c2f50198d8f71f0e858f2))
610 | * completed setModelMarkers ([86079ad](https://github.com/vuejs/repl/commit/86079ad2030559052cef8b32e0d4a301a62b5f65))
611 | * implemented provideCodeActions, resolveCodeAction ([1d37f55](https://github.com/vuejs/repl/commit/1d37f55eacb4124171813d1db777496aa1bddeb3))
612 | * implemented provideCodeLenses, resolveCodeLens ([bddb65b](https://github.com/vuejs/repl/commit/bddb65b19053f069de3a3366666e3db1fd6bd6b1))
613 | * implemented provideDeclaration ([8e437f0](https://github.com/vuejs/repl/commit/8e437f0e8796ba3b8d7a19478fc1b9bcde6f2e43))
614 | * implemented provideDocumentColors, provideColorPresentations ([ddde114](https://github.com/vuejs/repl/commit/ddde1140945ac819765d93c7e33b07046fca55a6))
615 | * implemented provideDocumentFormattingEdits ([73f046d](https://github.com/vuejs/repl/commit/73f046d80cd17c9dea7051f6d488e3b757366ad9))
616 | * implemented provideDocumentHighlights ([f782496](https://github.com/vuejs/repl/commit/f7824960cb34f4ee2b58ad37a2af1a9364367b7a))
617 | * implemented provideDocumentRangeFormattingEdits ([0e64a81](https://github.com/vuejs/repl/commit/0e64a81a57e2e973d95bcf3bba03c43f48abc508))
618 | * implemented provideDocumentSymbols ([1cda6c6](https://github.com/vuejs/repl/commit/1cda6c638223c3d26f1b07deb24c6ba7b699014b))
619 | * implemented provideFoldingRanges ([dd083d3](https://github.com/vuejs/repl/commit/dd083d3b246b5db8e7a158585e0fc912c9dd03ce))
620 | * implemented provideImplementation ([49d8d7e](https://github.com/vuejs/repl/commit/49d8d7e52b34e94956a1e93bd7c98452d96831dd))
621 | * implemented provideInlayHints ([61d18c9](https://github.com/vuejs/repl/commit/61d18c97d9ea309a48ff50188661bcbfe97312a4))
622 | * implemented provideLinkedEditingRanges ([1046ac8](https://github.com/vuejs/repl/commit/1046ac8a29045b7a326002cb3fec9387675d9b33))
623 | * implemented provideLinks ([3bc4063](https://github.com/vuejs/repl/commit/3bc406352ac7ce697e7d6c4b83f7ff737adede08))
624 | * implemented provideOnTypeFormattingEdits ([f50e1c9](https://github.com/vuejs/repl/commit/f50e1c9c1ef7ed0ea02d51cdaaa5802f69911c7f))
625 | * implemented provideReferences ([6b05d17](https://github.com/vuejs/repl/commit/6b05d1726d3e06c36008b847a55978e1e7b99843))
626 | * implemented provideRenameEdits ([5ca6318](https://github.com/vuejs/repl/commit/5ca63181a2beebcc180282cd70f6732e572fd51d))
627 | * implemented provideSelectionRanges ([2037d73](https://github.com/vuejs/repl/commit/2037d73ded9944b10a4379d369aeec06a304df97))
628 | * implemented provideTypeDefinition ([b80bb0d](https://github.com/vuejs/repl/commit/b80bb0da90481cbebb214abf4a333daa7c4a42e9))
629 | * implemented resolveCompletionItem ([3ef6ffd](https://github.com/vuejs/repl/commit/3ef6ffdf58a66f74a8903b7479b43195b13aeb69))
630 | * **monaco:** support to keep selection and cursor position ([#99](https://github.com/vuejs/repl/issues/99)) ([db8c1bd](https://github.com/vuejs/repl/commit/db8c1bdd13ac44c15336795387aa8e7a449dfd74))
631 | * pass vue dts module version from store ([2a0dfc0](https://github.com/vuejs/repl/commit/2a0dfc011a547d61523e5f64d882e5ed940bbb30))
632 | * support auto close tag / auto .value ([f765d54](https://github.com/vuejs/repl/commit/f765d54a69ef6aca5586873d19cf3f666adeb0c5))
633 | * support omitting ts/js extensions ([130a137](https://github.com/vuejs/repl/commit/130a137dd9640ea912e68d27e390dd03664e2699))
634 | * upgrade volar ([0783d93](https://github.com/vuejs/repl/commit/0783d93fdd310e92ae3b2e22b0dc6ea78f93beaf))
635 | * upgrade volar ([0aac8d1](https://github.com/vuejs/repl/commit/0aac8d11632e0963e0ae3de4e02cf47a16deec60))
636 | * use monaco ([e833cf1](https://github.com/vuejs/repl/commit/e833cf14d447063654a02a83ba12fd23c8619c77))
637 |
638 |
639 | ### Performance Improvements
640 |
641 | * cache `ts.ScriptSnapshot.fromString` ([6f34b78](https://github.com/vuejs/repl/commit/6f34b78d7d637d8fc238ca68c8beb87884f110b0))
642 | * preset failed node_modules paths to speed up dts acquisition ([e93f049](https://github.com/vuejs/repl/commit/e93f0499719595aad61473b0b7819ece1b46818e))
643 |
644 |
645 |
646 | # [1.5.0](https://github.com/vuejs/repl/compare/v1.4.1...v1.5.0) (2023-06-14)
647 |
648 |
649 | ### Bug Fixes
650 |
651 | * add ts and json mode ([#37](https://github.com/vuejs/repl/issues/37)) ([0e467af](https://github.com/vuejs/repl/commit/0e467afbb52c759fdad0a2bfc263812b0df285c5))
652 | * console logging for component instance proxies ([#62](https://github.com/vuejs/repl/issues/62)) ([bb0e143](https://github.com/vuejs/repl/commit/bb0e1430bff586b5505c3e9d11e8331359ee23d2))
653 | * css update in ssr mode ([3b7e511](https://github.com/vuejs/repl/commit/3b7e51126dd32e4ebf36b9bd492f1c117ac9de69)), closes [#91](https://github.com/vuejs/repl/issues/91) [#92](https://github.com/vuejs/repl/issues/92)
654 | * Fix reason.message not existing case ([#54](https://github.com/vuejs/repl/issues/54)) ([2508030](https://github.com/vuejs/repl/commit/2508030241504d750a3226eb9a70fddd45d3299d))
655 | * improve code gen when using cssVars in SSR ([#85](https://github.com/vuejs/repl/issues/85)) ([7e2bcc8](https://github.com/vuejs/repl/commit/7e2bcc864360e302d8b2a48e6904b7ec6c099f3f))
656 | * improve code with optional chain ([#72](https://github.com/vuejs/repl/issues/72)) ([b8caeae](https://github.com/vuejs/repl/commit/b8caeaef0368609fa3c41e992304d21d526de08c))
657 | * prevent opening new tab for a tags with javascript href ([#94](https://github.com/vuejs/repl/issues/94)) ([64906a5](https://github.com/vuejs/repl/commit/64906a529cc48869791e663ba6d203baed236f6f))
658 | * process all files when dynamic import ([#60](https://github.com/vuejs/repl/issues/60)) ([7049ae0](https://github.com/vuejs/repl/commit/7049ae006f8687d2dafce38b7f54d7281410062a))
659 |
660 |
661 | ### Features
662 |
663 | * add `sublime` keymap ([#45](https://github.com/vuejs/repl/issues/45)) ([29263d8](https://github.com/vuejs/repl/commit/29263d83d2d28e2ea3fc85c59de6d6d7ef92cca6))
664 | * add file renaming ([#63](https://github.com/vuejs/repl/issues/63)) ([eb41c3a](https://github.com/vuejs/repl/commit/eb41c3a180eb720ba0959ba2da8064442f1b25e6))
665 | * add search and replace ([#67](https://github.com/vuejs/repl/issues/67)) ([4ca3d94](https://github.com/vuejs/repl/commit/4ca3d94c98ed2029ccd61197780d45f348b2fcde))
666 | * local JSON files ([#82](https://github.com/vuejs/repl/issues/82)) ([db076eb](https://github.com/vuejs/repl/commit/db076eb2b07e104ef460d7e2bd99769b5653e1a5))
667 | * support for sandbox page customization ([#42](https://github.com/vuejs/repl/issues/42)) ([a22b969](https://github.com/vuejs/repl/commit/a22b96968894dcaf4fa096edf8a1dd7d7f903e5e))
668 |
669 |
670 |
671 | ## [1.4.1](https://github.com/vuejs/repl/compare/v1.4.0...v1.4.1) (2023-04-21)
672 |
673 |
674 |
675 | # [1.4.0](https://github.com/vuejs/repl/compare/v1.3.6...v1.4.0) (2023-04-13)
676 |
677 |
678 | ### Features
679 |
680 | * provide fs option to support 3.3 external type resolving ([f0e826a](https://github.com/vuejs/repl/commit/f0e826a1ff9eae7c008f2b92b4af35a518dd0c7f))
681 |
682 |
683 |
684 | ## [1.3.6](https://github.com/vuejs/repl/compare/v1.3.5...v1.3.6) (2023-04-13)
685 |
686 |
687 | ### Bug Fixes
688 |
689 | * **types:** make sfc options partial ([9916f28](https://github.com/vuejs/repl/commit/9916f2862b327891604f3282fedf626759694e2c))
690 |
691 |
692 |
693 | ## [1.3.5](https://github.com/vuejs/repl/compare/v1.3.4...v1.3.5) (2023-04-06)
694 |
695 |
696 | ### Bug Fixes
697 |
698 | * avoid including vue in import map if using default URLs ([37ce32b](https://github.com/vuejs/repl/commit/37ce32b107864332eeebbc406a817d78ae8d982a))
699 |
700 |
701 |
702 | ## [1.3.4](https://github.com/vuejs/repl/compare/v1.3.3...v1.3.4) (2023-04-06)
703 |
704 |
705 | ### Bug Fixes
706 |
707 | * fix legacy domain in import maps ([7e7c7f9](https://github.com/vuejs/repl/commit/7e7c7f9dd62995f2f27448e72effb4c8fe879d72))
708 |
709 |
710 |
711 | ## [1.3.3](https://github.com/vuejs/repl/compare/v1.3.2...v1.3.3) (2023-03-17)
712 |
713 |
714 | ### Bug Fixes
715 |
716 | * ignore polyfill error in Safari ([39f4ab1](https://github.com/vuejs/repl/commit/39f4ab1956af85383e6616eafec3efc616313d28))
717 |
718 |
719 |
720 | ## [1.3.2](https://github.com/vuejs/repl/compare/v1.3.1...v1.3.2) (2022-09-27)
721 |
722 |
723 | ### Bug Fixes
724 |
725 | * reset sandbox when changing files for safari compat ([68a6197](https://github.com/vuejs/repl/commit/68a6197bbfb88dc74ec317ae50e3f686cbfeb081)), closes [vuejs/docs#1973](https://github.com/vuejs/docs/issues/1973)
726 |
727 |
728 |
729 | ## [1.3.1](https://github.com/vuejs/repl/compare/v1.3.0...v1.3.1) (2022-09-27)
730 |
731 |
732 |
733 | # [1.3.0](https://github.com/vuejs/repl/compare/v1.2.4...v1.3.0) (2022-06-26)
734 |
735 |
736 |
737 | ## [1.2.4](https://github.com/vuejs/repl/compare/v1.2.3...v1.2.4) (2022-06-26)
738 |
739 |
740 | ### Bug Fixes
741 |
742 | * compile error when no script ([#38](https://github.com/vuejs/repl/issues/38)) ([6b9b7bc](https://github.com/vuejs/repl/commit/6b9b7bc9ea3f89772eaf1807e3b7478d39f3ef9c))
743 |
744 |
745 | ### Features
746 |
747 | * export Preview component ([#39](https://github.com/vuejs/repl/issues/39)) ([0b93cd6](https://github.com/vuejs/repl/commit/0b93cd66f5dc0beb2e44f271efa3868a155bff21))
748 | * gzip serialized state ([#43](https://github.com/vuejs/repl/issues/43)) ([b12eb88](https://github.com/vuejs/repl/commit/b12eb885deb080246d372495f443fe543de1eb6d))
749 |
750 |
751 |
752 | ## [1.2.3](https://github.com/vuejs/repl/compare/v1.2.2...v1.2.3) (2022-05-25)
753 |
754 |
755 | ### Bug Fixes
756 |
757 | * also reset import map when resetting to defauilt vue version ([5e89f07](https://github.com/vuejs/repl/commit/5e89f074ea5d33b301e079c5f4fe7860e1e5ca82))
758 | * improve new filename logic ([9647666](https://github.com/vuejs/repl/commit/9647666554407b32f16b8b5581333542769a5ea0))
759 | * warn versions that do not support in browser SSR ([01cb5b2](https://github.com/vuejs/repl/commit/01cb5b20cd15c3dcbe9f1b6d3dbc8797702924e9))
760 |
761 |
762 |
763 | ## [1.2.2](https://github.com/vuejs/repl/compare/v1.2.1...v1.2.2) (2022-05-25)
764 |
765 |
766 | ### Bug Fixes
767 |
768 | * do not start compiling until sfc options are set ([b6f86d9](https://github.com/vuejs/repl/commit/b6f86d920d22d83fde3bb77b11e8f44fff1a244d))
769 |
770 |
771 |
772 | ## [1.2.1](https://github.com/vuejs/repl/compare/v1.2.0...v1.2.1) (2022-05-25)
773 |
774 |
775 | ### Bug Fixes
776 |
777 | * fix html initialization in ssr mode ([152f2fa](https://github.com/vuejs/repl/commit/152f2fad88fa87fb617a8a69ff8f9f2c1b1eba33))
778 |
779 |
780 |
781 | # [1.2.0](https://github.com/vuejs/repl/compare/v1.1.2...v1.2.0) (2022-05-25)
782 |
783 |
784 | ### Bug Fixes
785 |
786 | * avoid using native crypto ([c22e216](https://github.com/vuejs/repl/commit/c22e216b1c6d8bbce3cbb4376d82ce15ce149433)), closes [#25](https://github.com/vuejs/repl/issues/25)
787 |
788 |
789 | ### Features
790 |
791 | * **FileSelector:** add an increment counter for new files ([#36](https://github.com/vuejs/repl/issues/36)) ([63b8f22](https://github.com/vuejs/repl/commit/63b8f22a991984ce1ce6c56d14ae4f35f8b4a436))
792 | * support ssr + hydration ([098aa89](https://github.com/vuejs/repl/commit/098aa8992ad860c8529fb285552c6c26e7518e9e))
793 |
794 |
795 |
796 | ## [1.1.2](https://github.com/vuejs/repl/compare/v1.1.1...v1.1.2) (2022-05-20)
797 |
798 |
799 | ### Bug Fixes
800 |
801 | * apply TS transform to template when inine is disabled ([ec2ae81](https://github.com/vuejs/repl/commit/ec2ae811bd25da4be74b9df3bb8fcf9ba5d34cfb))
802 |
803 |
804 |
805 | ## [1.1.1](https://github.com/vuejs/repl/compare/v1.1.0...v1.1.1) (2022-05-17)
806 |
807 |
808 | ### Bug Fixes
809 |
810 | * adding file using enter emits error ([#23](https://github.com/vuejs/repl/issues/23)) ([918de7f](https://github.com/vuejs/repl/commit/918de7f3646a24db083e54301d6ac5c3a970c0df))
811 |
812 |
813 |
814 | # [1.1.0](https://github.com/vuejs/repl/compare/v1.0.1...v1.1.0) (2022-05-17)
815 |
816 |
817 |
818 | ## [1.0.1](https://github.com/vuejs/repl/compare/f8bb46f969860539e3105ff56d092f0184a70eba...v1.0.1) (2022-05-17)
819 |
820 |
821 | ### Bug Fixes
822 |
823 | * also generate render function if inline mode is disabled ([9a325bb](https://github.com/vuejs/repl/commit/9a325bbf66b61403cd4df5ace31d0e7e1532fddf))
824 | * avoid reloading the iframe when switching output tabs ([20bde55](https://github.com/vuejs/repl/commit/20bde550e481c0a9c9218f8a583eae7b27ca42d2))
825 | * css double # ([#14](https://github.com/vuejs/repl/issues/14)) ([8bcf7f0](https://github.com/vuejs/repl/commit/8bcf7f0f22553214f7936863de3d9780272781b0))
826 | * fix module instantiation order ([879f084](https://github.com/vuejs/repl/commit/879f08495c061afa11e058a3e059365fe09277c6))
827 | * fix rewriteDefault TS edge case ([d277d7f](https://github.com/vuejs/repl/commit/d277d7f50113c45b8ae71afcda9aa369c64fba32))
828 | * fix setFiles with multi files cross imports ([424e00d](https://github.com/vuejs/repl/commit/424e00d2ac50636b3a2a9739620435b156f1a94a))
829 | * force app name ([18863af](https://github.com/vuejs/repl/commit/18863af803922f3966a80922db7c8a45a0cdd78d))
830 | * small screen error msg covered code button ([#18](https://github.com/vuejs/repl/issues/18)) ([02da79d](https://github.com/vuejs/repl/commit/02da79d0a238b8777fcd95675c8c5dbd1b626fd4))
831 | * toggler should be absolute ([f8bb46f](https://github.com/vuejs/repl/commit/f8bb46f969860539e3105ff56d092f0184a70eba))
832 | * update import map when setting vue versions ([15cc696](https://github.com/vuejs/repl/commit/15cc696054b49fe5ea6879b9492b96cca611c945))
833 |
834 |
835 | ### Features
836 |
837 | * add hidden file ([#17](https://github.com/vuejs/repl/issues/17)) ([35b6f1a](https://github.com/vuejs/repl/commit/35b6f1a38611e31b9adbe7540d789be144e33bdc))
838 | * allow starting on a specific view ([#15](https://github.com/vuejs/repl/issues/15)) ([7e63511](https://github.com/vuejs/repl/commit/7e635110bb5e11e8103b66c5d347cf959be8bd55))
839 | * export compileFile ([#13](https://github.com/vuejs/repl/issues/13)) ([60db549](https://github.com/vuejs/repl/commit/60db54905699e005d3117a693410c0cd50f154fe))
840 | * file-selector add horizontal scroll ([#10](https://github.com/vuejs/repl/issues/10)) ([d0c961e](https://github.com/vuejs/repl/commit/d0c961e7b20939f0e028fd0cb89ce75123f32aa7))
841 | * support passing in compiler-sfc options ([f6c7049](https://github.com/vuejs/repl/commit/f6c7049f9bc4a5e1dd3e1c1948ba2ecb43fad3c3))
842 | * support ts in template expressions ([a1e9881](https://github.com/vuejs/repl/commit/a1e98814699c020a2d82c8c5aad664e99bd6ef52))
843 | * vertical mode ([d59bb6c](https://github.com/vuejs/repl/commit/d59bb6cd0eb0e03fa548595f5c64b990cecd133e))
844 |
845 |
846 |
847 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2021-present, Yuxi (Evan) You
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
13 | all 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
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # @vue/repl
2 |
3 | Vue SFC REPL as a Vue 3 component.
4 |
5 | ## Basic Usage
6 |
7 | **Note: `@vue/repl` >= 2 now supports Monaco Editor, but also requires explicitly passing in the editor to be used for tree-shaking.**
8 |
9 | ```ts
10 | // vite.config.ts
11 | import { defineConfig } from 'vite'
12 | export default defineConfig({
13 | optimizeDeps: {
14 | exclude: ['@vue/repl'],
15 | },
16 | // ...
17 | })
18 | ```
19 |
20 | ### With CodeMirror Editor
21 |
22 | Basic editing experience with no intellisense. Lighter weight, fewer network requests, better for embedding use cases.
23 |
24 | ```vue
25 |
31 |
32 |
33 |
34 |
35 | ```
36 |
37 | ### With Monaco Editor
38 |
39 | With Volar support, autocomplete, type inference, and semantic highlighting. Heavier bundle, loads dts files from CDN, better for standalone use cases.
40 |
41 | ```vue
42 |
48 |
49 |
50 |
51 |
52 | ```
53 |
54 | ## Advanced Usage
55 |
56 | Customize the behavior of the REPL by manually initializing the store.
57 |
58 | See [v4 Migration Guide](https://github.com/vuejs/repl/releases/tag/v4.0.0)
59 |
60 | ```vue
61 |
105 |
106 |
107 |
108 |
109 | ```
110 |
111 | Use only the Preview without the editor
112 |
113 | ```vue
114 |
130 |
131 |
132 |
133 |
134 | ```
135 |
--------------------------------------------------------------------------------
/eslint.config.js:
--------------------------------------------------------------------------------
1 | import eslint from '@eslint/js'
2 | import tseslint from 'typescript-eslint'
3 | import pluginVue from 'eslint-plugin-vue'
4 |
5 | export default tseslint.config(
6 | { ignores: ['**/node_modules', '**/dist'] },
7 | eslint.configs.recommended,
8 | tseslint.configs.base,
9 | ...pluginVue.configs['flat/recommended'],
10 | {
11 | files: ['**/*.vue'],
12 | languageOptions: {
13 | parserOptions: {
14 | parser: '@typescript-eslint/parser',
15 | },
16 | },
17 | },
18 | {
19 | rules: {
20 | 'no-debugger': 'error',
21 | 'no-console': ['error', { allow: ['warn', 'error', 'info', 'clear'] }],
22 | 'no-unused-vars': 'off',
23 | 'no-undef': 'off',
24 | 'prefer-const': 'error',
25 | 'sort-imports': ['error', { ignoreDeclarationSort: true }],
26 | 'no-duplicate-imports': 'error',
27 | // This rule enforces the preference for using '@ts-expect-error' comments in TypeScript
28 | // code to indicate intentional type errors, improving code clarity and maintainability.
29 | '@typescript-eslint/prefer-ts-expect-error': 'error',
30 | // Enforce the use of 'import type' for importing types
31 | '@typescript-eslint/consistent-type-imports': [
32 | 'error',
33 | {
34 | fixStyle: 'inline-type-imports',
35 | disallowTypeAnnotations: false,
36 | },
37 | ],
38 | // Enforce the use of top-level import type qualifier when an import only has specifiers with inline type qualifiers
39 | '@typescript-eslint/no-import-type-side-effects': 'error',
40 | 'vue/max-attributes-per-line': 'off',
41 | 'vue/singleline-html-element-content-newline': 'off',
42 | 'vue/multi-word-component-names': 'off',
43 | 'vue/html-self-closing': [
44 | 'error',
45 | {
46 | html: { component: 'always', normal: 'always', void: 'any' },
47 | math: 'always',
48 | svg: 'always',
49 | },
50 | ],
51 | },
52 | },
53 | )
54 |
--------------------------------------------------------------------------------
/index-dist.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vue SFC Playground
8 |
17 |
18 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vue SFC Playground
8 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@vue/repl",
3 | "version": "4.6.1",
4 | "description": "Vue component for editing Vue components",
5 | "packageManager": "pnpm@9.15.5",
6 | "type": "module",
7 | "main": "dist/ssr-stub.js",
8 | "module": "dist/vue-repl.js",
9 | "files": [
10 | "dist"
11 | ],
12 | "types": "dist/vue-repl.d.ts",
13 | "exports": {
14 | ".": {
15 | "types": "./dist/vue-repl.d.ts",
16 | "import": "./dist/vue-repl.js",
17 | "require": "./dist/ssr-stub.js"
18 | },
19 | "./monaco-editor": {
20 | "types": "./dist/monaco-editor.d.ts",
21 | "import": "./dist/monaco-editor.js",
22 | "require": null
23 | },
24 | "./codemirror-editor": {
25 | "types": "./dist/codemirror-editor.d.ts",
26 | "import": "./dist/codemirror-editor.js",
27 | "require": null
28 | },
29 | "./core": {
30 | "types": "./dist/core.d.ts",
31 | "import": "./dist/core.js",
32 | "require": null
33 | },
34 | "./package.json": "./package.json",
35 | "./style.css": "./dist/vue-repl.css",
36 | "./dist/style.css": "./dist/vue-repl.css"
37 | },
38 | "typesVersions": {
39 | "*": {
40 | "*": [
41 | "./dist/*",
42 | "./*"
43 | ]
44 | }
45 | },
46 | "publishConfig": {
47 | "tag": "latest"
48 | },
49 | "scripts": {
50 | "dev": "vite",
51 | "build": "vite build",
52 | "build-preview": "vite build -c vite.preview.config.ts",
53 | "format": "prettier --write .",
54 | "lint": "eslint .",
55 | "typecheck": "vue-tsc --noEmit",
56 | "release": "bumpp --all",
57 | "version": "conventional-changelog -p angular -i CHANGELOG.md -s -r 0",
58 | "prepublishOnly": "npm run build"
59 | },
60 | "simple-git-hooks": {
61 | "pre-commit": "pnpm exec lint-staged --concurrent false"
62 | },
63 | "lint-staged": {
64 | "*": [
65 | "prettier --write --cache --ignore-unknown"
66 | ]
67 | },
68 | "repository": {
69 | "type": "git",
70 | "url": "git+https://github.com/vuejs/repl.git"
71 | },
72 | "author": "Evan You",
73 | "license": "MIT",
74 | "bugs": {
75 | "url": "https://github.com/vuejs/repl/issues"
76 | },
77 | "homepage": "https://github.com/vuejs/repl#readme",
78 | "devDependencies": {
79 | "@babel/standalone": "^7.26.9",
80 | "@babel/types": "^7.26.9",
81 | "@eslint/js": "^9.20.0",
82 | "@jridgewell/gen-mapping": "^0.3.8",
83 | "@jridgewell/trace-mapping": "^0.3.25",
84 | "@rollup/plugin-replace": "^6.0.2",
85 | "@shikijs/monaco": "^1.29.2",
86 | "@types/babel__standalone": "^7.1.9",
87 | "@types/codemirror": "^5.60.15",
88 | "@types/hash-sum": "^1.0.2",
89 | "@types/node": "^22.13.4",
90 | "@vitejs/plugin-vue": "^5.2.1",
91 | "@volar/jsdelivr": "~2.4.11",
92 | "@volar/monaco": "~2.4.11",
93 | "@vue/babel-plugin-jsx": "^1.2.5",
94 | "@vue/language-service": "~2.2.2",
95 | "assert": "^2.1.0",
96 | "bumpp": "^9.11.1",
97 | "codemirror": "^5.65.18",
98 | "conventional-changelog-cli": "^5.0.0",
99 | "eslint": "^9.20.1",
100 | "eslint-plugin-vue": "^9.32.0",
101 | "fflate": "^0.8.2",
102 | "hash-sum": "^2.0.0",
103 | "lint-staged": "^15.4.3",
104 | "monaco-editor-core": "^0.52.2",
105 | "prettier": "^3.5.1",
106 | "shiki": "^1.29.2",
107 | "simple-git-hooks": "^2.11.1",
108 | "source-map-js": "^1.2.1",
109 | "sucrase": "^3.35.0",
110 | "typescript": "^5.7.3",
111 | "typescript-eslint": "^8.24.1",
112 | "vite": "^6.1.0",
113 | "vite-plugin-dts": "^4.5.0",
114 | "vscode-uri": "^3.1.0",
115 | "vue": "^3.5.15",
116 | "vue-tsc": "~2.2.2"
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/src/Message.vue:
--------------------------------------------------------------------------------
1 |
32 |
33 |
34 |
35 |
40 |
{{ formatMessage(err || warn!) }}
41 |
42 |
43 |
44 |
45 |
46 |
129 |
--------------------------------------------------------------------------------
/src/Repl.vue:
--------------------------------------------------------------------------------
1 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
116 |
117 |
118 |
119 |
120 |
121 |
158 |
--------------------------------------------------------------------------------
/src/SplitPane.vue:
--------------------------------------------------------------------------------
1 |
61 |
62 |
63 |
76 |
83 |
87 |
88 | {{ `${state.viewWidth}px x ${state.viewHeight}px` }}
89 |
90 |
91 |
92 |
93 |
100 |
101 |
102 |
103 |
221 |
--------------------------------------------------------------------------------
/src/codemirror/CodeMirror.vue:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
103 |
104 |
118 |
--------------------------------------------------------------------------------
/src/codemirror/codemirror.css:
--------------------------------------------------------------------------------
1 | /* BASICS */
2 |
3 | .CodeMirror {
4 | color: var(--symbols);
5 | --symbols: #777;
6 | --base: #545281;
7 | --comment: hsl(210, 25%, 60%);
8 | --keyword: #af4ab1;
9 | --variable: var(--base);
10 | --function: #c25205;
11 | --string: #2ba46d;
12 | --number: #c25205;
13 | --tags: #dd0000;
14 | --brackets: var(--comment);
15 | --qualifier: #ff6032;
16 | --important: var(--string);
17 | --attribute: #9c3eda;
18 | --property: #6182b8;
19 |
20 | --selected-bg: #d7d4f0;
21 | --selected-bg-non-focus: #d9d9d9;
22 | --cursor: #000;
23 |
24 | direction: ltr;
25 | font-family: var(--font-code);
26 | height: auto;
27 | }
28 |
29 | .dark .CodeMirror {
30 | color: var(--symbols);
31 | --symbols: #89ddff;
32 | --base: #a6accd;
33 | --comment: #6d6d6d;
34 | --keyword: #89ddff;
35 | --string: #c3e88d;
36 | --variable: #82aaff;
37 | --number: #f78c6c;
38 | --tags: #f07178;
39 | --brackets: var(--symbols);
40 | --property: #f07178;
41 | --attribute: #c792ea;
42 | --cursor: #fff;
43 |
44 | --selected-bg: rgba(255, 255, 255, 0.1);
45 | --selected-bg-non-focus: rgba(255, 255, 255, 0.15);
46 | }
47 |
48 | /* PADDING */
49 |
50 | .CodeMirror-lines {
51 | padding: 4px 0; /* Vertical padding around content */
52 | }
53 | .CodeMirror pre {
54 | padding: 0 4px; /* Horizontal padding of content */
55 | }
56 |
57 | .CodeMirror-scrollbar-filler,
58 | .CodeMirror-gutter-filler {
59 | background-color: white; /* The little square between H and V scrollbars */
60 | }
61 |
62 | /* GUTTER */
63 |
64 | .CodeMirror-gutters {
65 | border-right: 1px solid var(--border);
66 | background-color: transparent;
67 | white-space: nowrap;
68 | }
69 | .CodeMirror-linenumber {
70 | padding: 0 3px 0 5px;
71 | min-width: 20px;
72 | text-align: right;
73 | color: var(--comment);
74 | white-space: nowrap;
75 | opacity: 0.6;
76 | }
77 |
78 | .CodeMirror-guttermarker {
79 | color: black;
80 | }
81 | .CodeMirror-guttermarker-subtle {
82 | color: #999;
83 | }
84 |
85 | /* FOLD GUTTER */
86 |
87 | .CodeMirror-foldmarker {
88 | color: #414141;
89 | text-shadow:
90 | #ff9966 1px 1px 2px,
91 | #ff9966 -1px -1px 2px,
92 | #ff9966 1px -1px 2px,
93 | #ff9966 -1px 1px 2px;
94 | font-family: arial;
95 | line-height: 0.3;
96 | cursor: pointer;
97 | }
98 | .CodeMirror-foldgutter {
99 | width: 0.7em;
100 | }
101 | .CodeMirror-foldgutter-open,
102 | .CodeMirror-foldgutter-folded {
103 | cursor: pointer;
104 | }
105 | .CodeMirror-foldgutter-open:after,
106 | .CodeMirror-foldgutter-folded:after {
107 | content: '>';
108 | font-size: 0.8em;
109 | opacity: 0.8;
110 | transition: transform 0.2s;
111 | display: inline-block;
112 | top: -0.1em;
113 | position: relative;
114 | transform: rotate(90deg);
115 | }
116 | .CodeMirror-foldgutter-folded:after {
117 | transform: none;
118 | }
119 |
120 | /* CURSOR */
121 |
122 | .CodeMirror-cursor {
123 | border-left: 1px solid var(--cursor);
124 | border-right: none;
125 | width: 0;
126 | }
127 | /* Shown when moving in bi-directional text */
128 | .CodeMirror div.CodeMirror-secondarycursor {
129 | border-left: 1px solid silver;
130 | }
131 | .cm-fat-cursor .CodeMirror-cursor {
132 | width: auto;
133 | border: 0 !important;
134 | background: #7e7;
135 | }
136 | .cm-fat-cursor div.CodeMirror-cursors {
137 | z-index: 1;
138 | }
139 | .cm-fat-cursor-mark {
140 | background-color: rgba(20, 255, 20, 0.5);
141 | -webkit-animation: blink 1.06s steps(1) infinite;
142 | -moz-animation: blink 1.06s steps(1) infinite;
143 | animation: blink 1.06s steps(1) infinite;
144 | }
145 | .cm-animate-fat-cursor {
146 | width: auto;
147 | border: 0;
148 | -webkit-animation: blink 1.06s steps(1) infinite;
149 | -moz-animation: blink 1.06s steps(1) infinite;
150 | animation: blink 1.06s steps(1) infinite;
151 | background-color: #7e7;
152 | }
153 | @-moz-keyframes blink {
154 | 0% {
155 | }
156 | 50% {
157 | background-color: transparent;
158 | }
159 | 100% {
160 | }
161 | }
162 | @-webkit-keyframes blink {
163 | 0% {
164 | }
165 | 50% {
166 | background-color: transparent;
167 | }
168 | 100% {
169 | }
170 | }
171 | @keyframes blink {
172 | 0% {
173 | }
174 | 50% {
175 | background-color: transparent;
176 | }
177 | 100% {
178 | }
179 | }
180 |
181 | .cm-tab {
182 | display: inline-block;
183 | text-decoration: inherit;
184 | }
185 |
186 | .CodeMirror-rulers {
187 | position: absolute;
188 | left: 0;
189 | right: 0;
190 | top: -50px;
191 | bottom: -20px;
192 | overflow: hidden;
193 | }
194 | .CodeMirror-ruler {
195 | border-left: 1px solid #ccc;
196 | top: 0;
197 | bottom: 0;
198 | position: absolute;
199 | }
200 |
201 | /* DEFAULT THEME */
202 | .cm-s-default.CodeMirror {
203 | background-color: transparent;
204 | }
205 | .cm-s-default .cm-header {
206 | color: blue;
207 | }
208 | .cm-s-default .cm-quote {
209 | color: #090;
210 | }
211 | .cm-negative {
212 | color: #d44;
213 | }
214 | .cm-positive {
215 | color: #292;
216 | }
217 | .cm-header,
218 | .cm-strong {
219 | font-weight: bold;
220 | }
221 | .cm-em {
222 | font-style: italic;
223 | }
224 | .cm-link {
225 | text-decoration: underline;
226 | }
227 | .cm-strikethrough {
228 | text-decoration: line-through;
229 | }
230 |
231 | .cm-s-default .cm-atom,
232 | .cm-s-default .cm-def,
233 | .cm-s-default .cm-variable-2,
234 | .cm-s-default .cm-variable-3,
235 | .cm-s-default .cm-punctuation {
236 | color: var(--base);
237 | }
238 | .cm-s-default .cm-property {
239 | color: var(--property);
240 | }
241 | .cm-s-default .cm-hr,
242 | .cm-s-default .cm-comment {
243 | color: var(--comment);
244 | }
245 | .cm-s-default .cm-attribute {
246 | color: var(--attribute);
247 | }
248 | .cm-s-default .cm-keyword {
249 | color: var(--keyword);
250 | }
251 | .cm-s-default .cm-variable {
252 | color: var(--variable);
253 | }
254 | .cm-s-default .cm-tag {
255 | color: var(--tags);
256 | }
257 | .cm-s-default .cm-bracket {
258 | color: var(--brackets);
259 | }
260 | .cm-s-default .cm-number {
261 | color: var(--number);
262 | }
263 | .cm-s-default .cm-string,
264 | .cm-s-default .cm-string-2 {
265 | color: var(--string);
266 | }
267 | .cm-s-default .cm-type {
268 | color: rgb(255, 208, 0);
269 | }
270 | .cm-s-default .cm-meta {
271 | color: #555;
272 | }
273 | .cm-s-default .cm-qualifier {
274 | color: var(--qualifier);
275 | }
276 | .cm-s-default .cm-builtin {
277 | color: #7539ff;
278 | }
279 | .cm-s-default .cm-link {
280 | color: var(--flash);
281 | }
282 | .cm-s-default .cm-error {
283 | color: #ff008c;
284 | }
285 | .cm-invalidchar {
286 | color: #ff008c;
287 | }
288 |
289 | .CodeMirror-composing {
290 | border-bottom: 2px solid;
291 | }
292 |
293 | /* Default styles for common addons */
294 |
295 | div.CodeMirror span.CodeMirror-matchingbracket {
296 | color: #0b0;
297 | }
298 | div.CodeMirror span.CodeMirror-nonmatchingbracket {
299 | color: #a22;
300 | }
301 | .CodeMirror-matchingtag {
302 | background: rgba(255, 150, 0, 0.3);
303 | }
304 | .CodeMirror-activeline-background {
305 | background: #e8f2ff;
306 | }
307 |
308 | /* STOP */
309 |
310 | /* The rest of this file contains styles related to the mechanics of
311 | the editor. You probably shouldn't touch them. */
312 |
313 | .CodeMirror {
314 | position: relative;
315 | overflow: hidden;
316 | background: white;
317 | }
318 |
319 | .CodeMirror-scroll {
320 | overflow: scroll !important; /* Things will break if this is overridden */
321 | /* 30px is the magic margin used to hide the element's real scrollbars */
322 | /* See overflow: hidden in .CodeMirror */
323 | margin-bottom: -30px;
324 | margin-right: -30px;
325 | padding-bottom: 30px;
326 | height: 100%;
327 | outline: none; /* Prevent dragging from highlighting the element */
328 | position: relative;
329 | }
330 | .CodeMirror-sizer {
331 | position: relative;
332 | border-right: 30px solid transparent;
333 | }
334 |
335 | /* The fake, visible scrollbars. Used to force redraw during scrolling
336 | before actual scrolling happens, thus preventing shaking and
337 | flickering artifacts. */
338 | .CodeMirror-vscrollbar,
339 | .CodeMirror-hscrollbar,
340 | .CodeMirror-scrollbar-filler,
341 | .CodeMirror-gutter-filler {
342 | position: absolute;
343 | z-index: 6;
344 | display: none;
345 | }
346 | .CodeMirror-vscrollbar {
347 | right: 0;
348 | top: 0;
349 | overflow-x: hidden;
350 | overflow-y: scroll;
351 | }
352 | .CodeMirror-hscrollbar {
353 | bottom: 0;
354 | left: 0;
355 | overflow-y: hidden;
356 | overflow-x: scroll;
357 | }
358 | .CodeMirror-scrollbar-filler {
359 | right: 0;
360 | bottom: 0;
361 | }
362 | .CodeMirror-gutter-filler {
363 | left: 0;
364 | bottom: 0;
365 | }
366 |
367 | .CodeMirror-gutters {
368 | position: absolute;
369 | left: 0;
370 | top: 0;
371 | min-height: 100%;
372 | z-index: 3;
373 | }
374 | .CodeMirror-gutter {
375 | white-space: normal;
376 | height: 100%;
377 | display: inline-block;
378 | vertical-align: top;
379 | margin-bottom: -30px;
380 | }
381 | .CodeMirror-gutter-wrapper {
382 | position: absolute;
383 | z-index: 4;
384 | background: none !important;
385 | border: none !important;
386 | }
387 | .CodeMirror-gutter-background {
388 | position: absolute;
389 | top: 0;
390 | bottom: 0;
391 | z-index: 4;
392 | }
393 | .CodeMirror-gutter-elt {
394 | position: absolute;
395 | cursor: default;
396 | z-index: 4;
397 | }
398 | .CodeMirror-gutter-wrapper ::selection {
399 | background-color: transparent;
400 | }
401 | .CodeMirror-gutter-wrapper ::-moz-selection {
402 | background-color: transparent;
403 | }
404 |
405 | .CodeMirror-lines {
406 | cursor: text;
407 | min-height: 1px; /* prevents collapsing before first draw */
408 | }
409 | .CodeMirror pre {
410 | /* Reset some styles that the rest of the page might have set */
411 | -moz-border-radius: 0;
412 | -webkit-border-radius: 0;
413 | border-radius: 0;
414 | border-width: 0;
415 | background: transparent;
416 | font-family: inherit;
417 | font-size: inherit;
418 | margin: 0;
419 | white-space: pre;
420 | word-wrap: normal;
421 | line-height: inherit;
422 | color: inherit;
423 | z-index: 2;
424 | position: relative;
425 | overflow: visible;
426 | -webkit-tap-highlight-color: transparent;
427 | -webkit-font-variant-ligatures: contextual;
428 | font-variant-ligatures: contextual;
429 | }
430 | .CodeMirror-wrap pre {
431 | word-wrap: break-word;
432 | white-space: pre-wrap;
433 | word-break: normal;
434 | }
435 |
436 | .CodeMirror-linebackground {
437 | position: absolute;
438 | left: 0;
439 | right: 0;
440 | top: 0;
441 | bottom: 0;
442 | z-index: 0;
443 | }
444 |
445 | .CodeMirror-linewidget {
446 | position: relative;
447 | z-index: 2;
448 | padding: 0.1px; /* Force widget margins to stay inside of the container */
449 | }
450 |
451 | .CodeMirror-rtl pre {
452 | direction: rtl;
453 | }
454 |
455 | .CodeMirror-code {
456 | outline: none;
457 | }
458 |
459 | /* Force content-box sizing for the elements where we expect it */
460 | .CodeMirror-scroll,
461 | .CodeMirror-sizer,
462 | .CodeMirror-gutter,
463 | .CodeMirror-gutters,
464 | .CodeMirror-linenumber {
465 | -moz-box-sizing: content-box;
466 | box-sizing: content-box;
467 | }
468 |
469 | .CodeMirror-measure {
470 | position: absolute;
471 | width: 100%;
472 | height: 0;
473 | overflow: hidden;
474 | visibility: hidden;
475 | }
476 |
477 | .CodeMirror-cursor {
478 | position: absolute;
479 | pointer-events: none;
480 | }
481 | .CodeMirror-measure pre {
482 | position: static;
483 | }
484 |
485 | div.CodeMirror-cursors {
486 | visibility: hidden;
487 | position: relative;
488 | z-index: 3;
489 | }
490 | div.CodeMirror-dragcursors {
491 | visibility: visible;
492 | }
493 |
494 | .CodeMirror-focused div.CodeMirror-cursors {
495 | visibility: visible;
496 | }
497 |
498 | .CodeMirror-selected {
499 | background: var(--selected-bg-non-focus);
500 | }
501 | .CodeMirror-focused .CodeMirror-selected {
502 | background: var(--selected-bg);
503 | }
504 | .CodeMirror-crosshair {
505 | cursor: crosshair;
506 | }
507 | .CodeMirror-line::selection,
508 | .CodeMirror-line > span::selection,
509 | .CodeMirror-line > span > span::selection {
510 | background: var(--selected-bg);
511 | }
512 | .CodeMirror-line::-moz-selection,
513 | .CodeMirror-line > span::-moz-selection,
514 | .CodeMirror-line > span > span::-moz-selection {
515 | background: var(--selected-bg);
516 | }
517 |
518 | .cm-searching {
519 | background-color: #ffa;
520 | background-color: rgba(255, 255, 0, 0.4);
521 | }
522 |
523 | /* Used to force a border model for a node */
524 | .cm-force-border {
525 | padding-right: 0.1px;
526 | }
527 |
528 | @media print {
529 | /* Hide the cursor when printing */
530 | .CodeMirror div.CodeMirror-cursors {
531 | visibility: hidden;
532 | }
533 | }
534 |
535 | /* See issue #2901 */
536 | .cm-tab-wrap-hack:after {
537 | content: '';
538 | }
539 |
540 | /* Help users use markselection to safely style text background */
541 | span.CodeMirror-selectedtext {
542 | background: none;
543 | }
544 |
545 | .CodeMirror-dialog {
546 | background-color: var(--bg);
547 | }
548 |
--------------------------------------------------------------------------------
/src/codemirror/codemirror.ts:
--------------------------------------------------------------------------------
1 | import CodeMirror from 'codemirror'
2 | import 'codemirror/addon/dialog/dialog.css'
3 | import './codemirror.css'
4 |
5 | // modes
6 | import 'codemirror/mode/javascript/javascript.js'
7 | import 'codemirror/mode/css/css.js'
8 | import 'codemirror/mode/htmlmixed/htmlmixed.js'
9 |
10 | // addons
11 | import 'codemirror/addon/edit/closebrackets.js'
12 | import 'codemirror/addon/edit/closetag.js'
13 | import 'codemirror/addon/comment/comment.js'
14 | import 'codemirror/addon/fold/foldcode.js'
15 | import 'codemirror/addon/fold/foldgutter.js'
16 | import 'codemirror/addon/fold/brace-fold.js'
17 | import 'codemirror/addon/fold/indent-fold.js'
18 | import 'codemirror/addon/fold/comment-fold.js'
19 | import 'codemirror/addon/search/search.js'
20 | import 'codemirror/addon/search/searchcursor.js'
21 | import 'codemirror/addon/dialog/dialog.js'
22 |
23 | // keymap
24 | import 'codemirror/keymap/sublime.js'
25 |
26 | export default CodeMirror
27 |
--------------------------------------------------------------------------------
/src/core.ts:
--------------------------------------------------------------------------------
1 | export {
2 | useStore,
3 | File,
4 | type SFCOptions,
5 | type StoreState,
6 | type Store,
7 | type ReplStore,
8 | } from './store'
9 | export { useVueImportMap, mergeImportMap, type ImportMap } from './import-map'
10 | export { compileFile } from './transform'
11 | export { version as languageToolsVersion } from '@vue/language-service/package.json'
12 |
--------------------------------------------------------------------------------
/src/editor/CodeMirrorEditor.vue:
--------------------------------------------------------------------------------
1 |
40 |
41 |
42 |
48 |
49 |
--------------------------------------------------------------------------------
/src/editor/EditorContainer.vue:
--------------------------------------------------------------------------------
1 |
35 |
36 |
37 |
38 |
39 |
44 |
45 |
46 |
47 |
52 |
57 |
58 |
59 |
60 |
61 |
82 |
--------------------------------------------------------------------------------
/src/editor/FileSelector.vue:
--------------------------------------------------------------------------------
1 |
113 |
114 |
115 |
121 |
122 | 0 && editFileName(file)"
128 | >
129 | {{ stripSrcPrefix(file) }}
130 |
131 |
135 |
136 |
137 |
142 | {{ pendingFilename }}
143 |
151 |
152 |
153 |
154 |
155 |
156 |
162 | tsconfig.json
163 |
164 |
170 | Import Map
171 |
172 |
173 |
174 |
175 |
176 |
294 |
--------------------------------------------------------------------------------
/src/editor/MonacoEditor.vue:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
25 |
26 |
--------------------------------------------------------------------------------
/src/editor/ToggleButton.vue:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
13 |
14 |
15 |
53 |
--------------------------------------------------------------------------------
/src/env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | declare module '*.vue' {
4 | import type { ComponentOptions } from 'vue'
5 | const comp: ComponentOptions
6 | export default comp
7 | }
8 |
--------------------------------------------------------------------------------
/src/import-map.ts:
--------------------------------------------------------------------------------
1 | import { computed, version as currentVersion, ref } from 'vue'
2 |
3 | export function useVueImportMap(
4 | defaults: {
5 | runtimeDev?: string | (() => string)
6 | runtimeProd?: string | (() => string)
7 | serverRenderer?: string | (() => string)
8 | vueVersion?: string | null
9 | } = {},
10 | ) {
11 | function normalizeDefaults(defaults?: string | (() => string)) {
12 | if (!defaults) return
13 | return typeof defaults === 'string' ? defaults : defaults()
14 | }
15 |
16 | const productionMode = ref(false)
17 | const vueVersion = ref(defaults.vueVersion || null)
18 | const importMap = computed(() => {
19 | const vue =
20 | (!vueVersion.value &&
21 | normalizeDefaults(
22 | productionMode.value ? defaults.runtimeProd : defaults.runtimeDev,
23 | )) ||
24 | `https://cdn.jsdelivr.net/npm/@vue/runtime-dom@${
25 | vueVersion.value || currentVersion
26 | }/dist/runtime-dom.esm-browser${productionMode.value ? `.prod` : ``}.js`
27 |
28 | const serverRenderer =
29 | (!vueVersion.value && normalizeDefaults(defaults.serverRenderer)) ||
30 | `https://cdn.jsdelivr.net/npm/@vue/server-renderer@${
31 | vueVersion.value || currentVersion
32 | }/dist/server-renderer.esm-browser.js`
33 | return {
34 | imports: {
35 | vue,
36 | 'vue/server-renderer': serverRenderer,
37 | },
38 | }
39 | })
40 |
41 | return {
42 | productionMode,
43 | importMap,
44 | vueVersion,
45 | defaultVersion: currentVersion,
46 | }
47 | }
48 |
49 | export interface ImportMap {
50 | imports?: Record
51 | scopes?: Record>
52 | }
53 |
54 | export function mergeImportMap(a: ImportMap, b: ImportMap): ImportMap {
55 | return {
56 | imports: { ...a.imports, ...b.imports },
57 | scopes: { ...a.scopes, ...b.scopes },
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | export { default as Repl, type Props as ReplProps } from './Repl.vue'
2 | export { default as Preview } from './output/Preview.vue'
3 | export { default as Sandbox, type SandboxProps } from './output/Sandbox.vue'
4 | export type { OutputModes } from './types'
5 | export * from './core'
6 |
--------------------------------------------------------------------------------
/src/jsx.ts:
--------------------------------------------------------------------------------
1 | import { transform } from '@babel/standalone'
2 | import jsx from '@vue/babel-plugin-jsx'
3 |
4 | export function transformJSX(src: string) {
5 | return transform(src, {
6 | plugins: [jsx],
7 | }).code!
8 | }
9 |
--------------------------------------------------------------------------------
/src/monaco/Monaco.vue:
--------------------------------------------------------------------------------
1 |
173 |
174 |
175 |
181 |
182 |
183 |
191 |
--------------------------------------------------------------------------------
/src/monaco/env.ts:
--------------------------------------------------------------------------------
1 | import * as volar from '@volar/monaco'
2 | import { Uri, editor, languages } from 'monaco-editor-core'
3 | import editorWorker from 'monaco-editor-core/esm/vs/editor/editor.worker?worker'
4 | import { watchEffect } from 'vue'
5 | import type { Store } from '../store'
6 | import { getOrCreateModel } from './utils'
7 | import type { CreateData } from './vue.worker'
8 | import vueWorker from './vue.worker?worker'
9 | import * as languageConfigs from './language-configs'
10 | import type { WorkerLanguageService } from '@volar/monaco/worker'
11 | import { debounce } from '../utils'
12 |
13 | let initted = false
14 | export function initMonaco(store: Store) {
15 | if (initted) return
16 | loadMonacoEnv(store)
17 |
18 | watchEffect(() => {
19 | // create a model for each file in the store
20 | for (const filename in store.files) {
21 | const file = store.files[filename]
22 | if (editor.getModel(Uri.parse(`file:///${filename}`))) continue
23 | getOrCreateModel(
24 | Uri.parse(`file:///${filename}`),
25 | file.language,
26 | file.code,
27 | )
28 | }
29 |
30 | // dispose of any models that are not in the store
31 | for (const model of editor.getModels()) {
32 | const uri = model.uri.toString()
33 | if (store.files[uri.substring('file:///'.length)]) continue
34 |
35 | if (uri.startsWith('file:///node_modules')) continue
36 | if (uri.startsWith('inmemory://')) continue
37 |
38 | model.dispose()
39 | }
40 | })
41 |
42 | initted = true
43 | }
44 |
45 | export class WorkerHost {
46 | onFetchCdnFile(uri: string, text: string) {
47 | getOrCreateModel(Uri.parse(uri), undefined, text)
48 | }
49 | }
50 |
51 | let disposeVue: undefined | (() => void)
52 | export async function reloadLanguageTools(store: Store) {
53 | disposeVue?.()
54 |
55 | let dependencies: Record = {
56 | ...store.dependencyVersion,
57 | }
58 |
59 | if (store.vueVersion) {
60 | dependencies = {
61 | ...dependencies,
62 | vue: store.vueVersion,
63 | '@vue/compiler-core': store.vueVersion,
64 | '@vue/compiler-dom': store.vueVersion,
65 | '@vue/compiler-sfc': store.vueVersion,
66 | '@vue/compiler-ssr': store.vueVersion,
67 | '@vue/reactivity': store.vueVersion,
68 | '@vue/runtime-core': store.vueVersion,
69 | '@vue/runtime-dom': store.vueVersion,
70 | '@vue/shared': store.vueVersion,
71 | }
72 | }
73 |
74 | if (store.typescriptVersion) {
75 | dependencies = {
76 | ...dependencies,
77 | typescript: store.typescriptVersion,
78 | }
79 | }
80 |
81 | const worker = editor.createWebWorker({
82 | moduleId: 'vs/language/vue/vueWorker',
83 | label: 'vue',
84 | host: new WorkerHost(),
85 | createData: {
86 | tsconfig: store.getTsConfig?.() || {},
87 | dependencies,
88 | } satisfies CreateData,
89 | })
90 | const languageId = ['vue', 'javascript', 'typescript']
91 | const getSyncUris = () =>
92 | Object.keys(store.files).map((filename) => Uri.parse(`file:///${filename}`))
93 |
94 | const { dispose: disposeMarkers } = volar.activateMarkers(
95 | worker,
96 | languageId,
97 | 'vue',
98 | getSyncUris,
99 | editor,
100 | )
101 | const { dispose: disposeAutoInsertion } = volar.activateAutoInsertion(
102 | worker,
103 | languageId,
104 | getSyncUris,
105 | editor,
106 | )
107 | const { dispose: disposeProvides } = await volar.registerProviders(
108 | worker,
109 | languageId,
110 | getSyncUris,
111 | languages,
112 | )
113 |
114 | disposeVue = () => {
115 | disposeMarkers()
116 | disposeAutoInsertion()
117 | disposeProvides()
118 | }
119 | }
120 |
121 | export interface WorkerMessage {
122 | event: 'init'
123 | tsVersion: string
124 | tsLocale?: string
125 | }
126 |
127 | export function loadMonacoEnv(store: Store) {
128 | ;(self as any).MonacoEnvironment = {
129 | async getWorker(_: any, label: string) {
130 | if (label === 'vue') {
131 | const worker = new vueWorker()
132 | const init = new Promise((resolve) => {
133 | worker.addEventListener('message', (data) => {
134 | if (data.data === 'inited') {
135 | resolve()
136 | }
137 | })
138 | worker.postMessage({
139 | event: 'init',
140 | tsVersion: store.typescriptVersion,
141 | tsLocale: store.locale,
142 | } satisfies WorkerMessage)
143 | })
144 | await init
145 | return worker
146 | }
147 | return new editorWorker()
148 | },
149 | }
150 | languages.register({ id: 'vue', extensions: ['.vue'] })
151 | languages.register({ id: 'javascript', extensions: ['.js'] })
152 | languages.register({ id: 'typescript', extensions: ['.ts'] })
153 | languages.register({ id: 'css', extensions: ['.css'] })
154 | languages.setLanguageConfiguration('vue', languageConfigs.vue)
155 | languages.setLanguageConfiguration('javascript', languageConfigs.js)
156 | languages.setLanguageConfiguration('typescript', languageConfigs.ts)
157 | languages.setLanguageConfiguration('css', languageConfigs.css)
158 |
159 | let languageToolsPromise: Promise | undefined
160 | store.reloadLanguageTools = debounce(async () => {
161 | ;(languageToolsPromise ||= reloadLanguageTools(store)).finally(() => {
162 | languageToolsPromise = undefined
163 | })
164 | }, 250)
165 | languages.onLanguage('vue', () => store.reloadLanguageTools!())
166 |
167 | // Support for go to definition
168 | editor.registerEditorOpener({
169 | openCodeEditor(_, resource) {
170 | if (resource.toString().startsWith('file:///node_modules')) {
171 | return true
172 | }
173 |
174 | const path = resource.path
175 | if (/^\//.test(path)) {
176 | const fileName = path.replace('/', '')
177 | if (fileName !== store.activeFile.filename) {
178 | store.setActive(fileName)
179 | return true
180 | }
181 | }
182 |
183 | return false
184 | },
185 | })
186 | }
187 |
--------------------------------------------------------------------------------
/src/monaco/highlight.ts:
--------------------------------------------------------------------------------
1 | import * as monaco from 'monaco-editor-core'
2 | import { createHighlighterCoreSync } from 'shiki/core'
3 | import { createJavaScriptRegexEngine } from 'shiki/engine-javascript.mjs'
4 | import { shikiToMonaco } from '@shikijs/monaco'
5 |
6 | import langVue from 'shiki/langs/vue.mjs'
7 | import langTsx from 'shiki/langs/tsx.mjs'
8 | import langJsx from 'shiki/langs/jsx.mjs'
9 | import themeDark from 'shiki/themes/dark-plus.mjs'
10 | import themeLight from 'shiki/themes/light-plus.mjs'
11 |
12 | let registered = false
13 | export function registerHighlighter() {
14 | if (!registered) {
15 | const highlighter = createHighlighterCoreSync({
16 | themes: [themeDark, themeLight],
17 | langs: [langVue, langTsx, langJsx],
18 | engine: createJavaScriptRegexEngine(),
19 | })
20 | monaco.languages.register({ id: 'vue' })
21 | shikiToMonaco(highlighter, monaco)
22 | registered = true
23 | }
24 |
25 | return {
26 | light: themeLight.name!,
27 | dark: themeDark.name!,
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/monaco/language-configs.ts:
--------------------------------------------------------------------------------
1 | import { languages } from 'monaco-editor-core'
2 |
3 | // export const html: languages.LanguageConfiguration = {
4 | // comments: {
5 | // blockComment: [''],
6 | // },
7 | // brackets: [
8 | // [''],
9 | // ['{', '}'],
10 | // ['(', ')'],
11 | // ],
12 | // autoClosingPairs: [
13 | // { open: '{', close: '}' },
14 | // { open: '[', close: ']' },
15 | // { open: '(', close: ')' },
16 | // { open: "'", close: "'" },
17 | // { open: '"', close: '"' },
18 | // { open: '', notIn: ['comment', 'string'] },
19 | // ],
20 | // surroundingPairs: [
21 | // { open: "'", close: "'" },
22 | // { open: '"', close: '"' },
23 | // { open: '{', close: '}' },
24 | // { open: '[', close: ']' },
25 | // { open: '(', close: ')' },
26 | // { open: '<', close: '>' },
27 | // ],
28 | // colorizedBracketPairs: [],
29 | // folding: {
30 | // markers: {
31 | // start: /^\s*/,
32 | // end: /^\s*/,
33 | // },
34 | // },
35 | // wordPattern: new RegExp(
36 | // '(-?\\d*\\.\\d\\w*)|([^\\`\\~\\!\\@\\$\\^\\&\\*\\(\\)\\=\\+\\[\\{\\]\\}\\\\\\|\\;\\:\\\'\\"\\,\\.\\<\\>\\/\\s]+)',
37 | // ),
38 | // onEnterRules: [
39 | // {
40 | // beforeText: new RegExp(
41 | // '<(?!(?:area|base|br|col|embed|hr|img|input|keygen|link|menuitem|meta|param|source|track|wbr))([_:\\w][_:\\w-.\\d]*)(?:(?:[^\'"/>]|"[^"]*"|\'[^\']*\')*?(?!\\/)>)[^<]*$',
42 | // 'i',
43 | // ),
44 | // afterText: new RegExp('^<\\/([_:\\w][_:\\w-.\\d]*)\\s*>', 'i'),
45 | // action: {
46 | // indentAction: languages.IndentAction.IndentOutdent,
47 | // },
48 | // },
49 | // {
50 | // beforeText: new RegExp(
51 | // '<(?!(?:area|base|br|col|embed|hr|img|input|keygen|link|menuitem|meta|param|source|track|wbr))([_:\\w][_:\\w-.\\d]*)(?:(?:[^\'"/>]|"[^"]*"|\'[^\']*\')*?(?!\\/)>)[^<]*$',
52 | // 'i',
53 | // ),
54 | // action: {
55 | // indentAction: languages.IndentAction.Indent,
56 | // },
57 | // },
58 | // ],
59 | // indentationRules: {
60 | // increaseIndentPattern: new RegExp(
61 | // '<(?!\\?|(?:area|base|br|col|frame|hr|html|img|input|keygen|link|menuitem|meta|param|source|track|wbr)\\b|[^>]*\\/>)([-_\\.A-Za-z0-9]+)(?=\\s|>)\\b[^>]*>(?!.*<\\/\\1>)|)|\\{[^}"\']*$',
62 | // ),
63 | // decreaseIndentPattern: new RegExp(
64 | // '^\\s*(<\\/(?!html)[-_\\.A-Za-z0-9]+\\b[^>]*>|-->|\\})',
65 | // ),
66 | // },
67 | // }
68 |
69 | export const css: languages.LanguageConfiguration = {
70 | comments: {
71 | blockComment: ['/*', '*/'],
72 | },
73 | brackets: [
74 | ['{', '}'],
75 | ['[', ']'],
76 | ['(', ')'],
77 | ],
78 | autoClosingPairs: [
79 | { open: '{', close: '}', notIn: ['string', 'comment'] },
80 | { open: '[', close: ']', notIn: ['string', 'comment'] },
81 | { open: '(', close: ')', notIn: ['string', 'comment'] },
82 | { open: '"', close: '"', notIn: ['string', 'comment'] },
83 | { open: "'", close: "'", notIn: ['string', 'comment'] },
84 | ],
85 | surroundingPairs: [
86 | {
87 | open: "'",
88 | close: "'",
89 | },
90 | {
91 | open: '"',
92 | close: '"',
93 | },
94 | {
95 | open: '{',
96 | close: '}',
97 | },
98 | {
99 | open: '[',
100 | close: ']',
101 | },
102 | {
103 | open: '(',
104 | close: ')',
105 | },
106 | ],
107 | folding: {
108 | markers: {
109 | start: new RegExp('^\\s*\\/\\*\\s*#region\\b\\s*(.*?)\\s*\\*\\/'),
110 | end: new RegExp('^\\s*\\/\\*\\s*#endregion\\b.*\\*\\/'),
111 | },
112 | },
113 | indentationRules: {
114 | increaseIndentPattern: new RegExp('(^.*\\{[^}]*$)'),
115 | decreaseIndentPattern: new RegExp('^\\s*\\}'),
116 | },
117 | wordPattern: new RegExp(
118 | '(#?-?\\d*\\.\\d\\w*%?)|(::?[\\w-]*(?=[^,{;]*[,{]))|(([@#.!])?[\\w-?]+%?|[@#!.])',
119 | ),
120 | }
121 |
122 | export const vue: languages.LanguageConfiguration = {
123 | comments: {
124 | blockComment: [''],
125 | },
126 | brackets: [
127 | [''],
128 | ['<', '>'],
129 | ['{', '}'],
130 | ['(', ')'],
131 | ],
132 | autoClosingPairs: [
133 | {
134 | open: '{',
135 | close: '}',
136 | },
137 | {
138 | open: '[',
139 | close: ']',
140 | },
141 | {
142 | open: '(',
143 | close: ')',
144 | },
145 | {
146 | open: "'",
147 | close: "'",
148 | },
149 | {
150 | open: '"',
151 | close: '"',
152 | },
153 | {
154 | open: '',
156 | notIn: ['comment', 'string'],
157 | },
158 | {
159 | open: '`',
160 | close: '`',
161 | notIn: ['string', 'comment'],
162 | },
163 | {
164 | open: '/**',
165 | close: ' */',
166 | notIn: ['string'],
167 | },
168 | ],
169 | autoCloseBefore: ';:.,=}])><`\'" \n\t',
170 | surroundingPairs: [
171 | {
172 | open: "'",
173 | close: "'",
174 | },
175 | {
176 | open: '"',
177 | close: '"',
178 | },
179 | {
180 | open: '{',
181 | close: '}',
182 | },
183 | {
184 | open: '[',
185 | close: ']',
186 | },
187 | {
188 | open: '(',
189 | close: ')',
190 | },
191 | {
192 | open: '<',
193 | close: '>',
194 | },
195 | {
196 | open: '`',
197 | close: '`',
198 | },
199 | ],
200 | colorizedBracketPairs: [],
201 | folding: {
202 | markers: {
203 | start: /^\s*/,
204 | end: /^\s*/,
205 | },
206 | },
207 | wordPattern:
208 | /(-?\d*\.\d\w*)|([^\`\@\~\!\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>/\?\s]+)/,
209 | onEnterRules: [
210 | {
211 | beforeText:
212 | /<(?!(?:area|base|br|col|embed|hr|img|input|keygen|link|menuitem|meta|param|source|track|wbr|script|style))([_:\w][_:\w-.\d]*)(?:(?:[^'"/>]|"[^"]*"|'[^']*')*?(?!\/)>)[^<]*$/i,
213 | afterText: /^<\/([_:\w][_:\w-.\d]*)\s*>/i,
214 | action: {
215 | indentAction: languages.IndentAction.IndentOutdent,
216 | },
217 | },
218 | {
219 | beforeText:
220 | /<(?!(?:area|base|br|col|embed|hr|img|input|keygen|link|menuitem|meta|param|source|track|wbr|script|style))([_:\w][_:\w-.\d]*)(?:(?:[^'"/>]|"[^"]*"|'[^']*')*?(?!\/)>)[^<]*$/i,
221 | action: {
222 | indentAction: languages.IndentAction.Indent,
223 | },
224 | },
225 | ],
226 | indentationRules: {
227 | increaseIndentPattern:
228 | /<(?!\?|(?:area|base|br|col|frame|hr|html|img|input|keygen|link|menuitem|meta|param|source|track|wbr|script|style)\b|[^>]*\/>)([-_\.A-Za-z0-9]+)(?=\s|>)\b[^>]*>(?!\s*\()(?!.*<\/\1>)|)|\{[^}"']*$/i,
229 | decreaseIndentPattern: /^\s*(<\/(?!html)[-_\.A-Za-z0-9]+\b[^>]*>|-->|\})/,
230 | },
231 | }
232 |
233 | export const js: languages.LanguageConfiguration = {
234 | comments: {
235 | lineComment: '//',
236 | blockComment: ['/*', '*/'],
237 | },
238 | brackets: [
239 | ['${', '}'],
240 | ['{', '}'],
241 | ['[', ']'],
242 | ['(', ')'],
243 | ],
244 | autoClosingPairs: [
245 | {
246 | open: '{',
247 | close: '}',
248 | },
249 | {
250 | open: '[',
251 | close: ']',
252 | },
253 | {
254 | open: '(',
255 | close: ')',
256 | },
257 | {
258 | open: "'",
259 | close: "'",
260 | notIn: ['string', 'comment'],
261 | },
262 | {
263 | open: '"',
264 | close: '"',
265 | notIn: ['string'],
266 | },
267 | {
268 | open: '`',
269 | close: '`',
270 | notIn: ['string', 'comment'],
271 | },
272 | {
273 | open: '/**',
274 | close: ' */',
275 | notIn: ['string'],
276 | },
277 | ],
278 | surroundingPairs: [
279 | {
280 | open: "'",
281 | close: "'",
282 | },
283 | {
284 | open: '"',
285 | close: '"',
286 | },
287 | {
288 | open: '{',
289 | close: '}',
290 | },
291 | {
292 | open: '[',
293 | close: ']',
294 | },
295 | {
296 | open: '(',
297 | close: ')',
298 | },
299 | {
300 | open: '<',
301 | close: '>',
302 | },
303 | {
304 | open: '`',
305 | close: '`',
306 | },
307 | ],
308 | autoCloseBefore: ';:.,=}])>` \n\t',
309 | folding: {
310 | markers: {
311 | start: /^\s*\/\/\s*#?region\b/,
312 | end: /^\s*\/\/\s*#?endregion\b/,
313 | },
314 | },
315 | wordPattern:
316 | /(-?\d*\.\d\w*)|([^\`\~\@\!\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>/\?\s]+)/,
317 | indentationRules: {
318 | decreaseIndentPattern: /^((?!.*?\/\*).*\*\/)?\s*[\}\]].*$/,
319 | increaseIndentPattern:
320 | /^((?!\/\/).)*(\{([^}"'`/]*|(\t|[ ])*\/\/.*)|\([^)"'`/]*|\[[^\]"'`/]*)$/,
321 | unIndentedLinePattern:
322 | /^(\t|[ ])*[ ]\*[^/]*\*\/\s*$|^(\t|[ ])*[ ]\*\/\s*$|^(\t|[ ])*[ ]\*([ ]([^\*]|\*(?!\/))*)?$/,
323 | },
324 | onEnterRules: [
325 | {
326 | beforeText: /^\s*\/\*\*(?!\/)([^\*]|\*(?!\/))*$/,
327 | afterText: /^\s*\*\/$/,
328 | action: {
329 | indentAction: languages.IndentAction.IndentOutdent,
330 | appendText: ' * ',
331 | },
332 | },
333 | {
334 | beforeText: /^\s*\/\*\*(?!\/)([^\*]|\*(?!\/))*$/,
335 | action: {
336 | indentAction: languages.IndentAction.None,
337 | appendText: ' * ',
338 | },
339 | },
340 | {
341 | beforeText: /^(\t|[ ])*[ ]\*([ ]([^\*]|\*(?!\/))*)?$/,
342 | previousLineText: /(?=^(\s*(\/\*\*|\*)).*)(?=(?!(\s*\*\/)))/,
343 | action: {
344 | indentAction: languages.IndentAction.None,
345 | appendText: '* ',
346 | },
347 | },
348 | {
349 | beforeText: /^(\t|[ ])*[ ]\*\/\s*$/,
350 | action: {
351 | indentAction: languages.IndentAction.None,
352 | removeText: 1,
353 | },
354 | },
355 | {
356 | beforeText: /^(\t|[ ])*[ ]\*[^/]*\*\/\s*$/,
357 | action: {
358 | indentAction: languages.IndentAction.None,
359 | removeText: 1,
360 | },
361 | },
362 | {
363 | beforeText: /^\s*(\bcase\s.+:|\bdefault:)$/,
364 | afterText: /^(?!\s*(\bcase\b|\bdefault\b))/,
365 | action: {
366 | indentAction: languages.IndentAction.Indent,
367 | },
368 | },
369 | {
370 | previousLineText: /^\s*(((else ?)?if|for|while)\s*\(.*\)\s*|else\s*)$/,
371 | beforeText: /^\s+([^{i\s]|i(?!f\b))/,
372 | action: {
373 | indentAction: languages.IndentAction.Outdent,
374 | },
375 | },
376 | ],
377 | }
378 |
379 | export const ts: languages.LanguageConfiguration = {
380 | comments: {
381 | lineComment: '//',
382 | blockComment: ['/*', '*/'],
383 | },
384 | brackets: [
385 | ['${', '}'],
386 | ['{', '}'],
387 | ['[', ']'],
388 | ['(', ')'],
389 | ],
390 | autoClosingPairs: [
391 | {
392 | open: '{',
393 | close: '}',
394 | },
395 | {
396 | open: '[',
397 | close: ']',
398 | },
399 | {
400 | open: '(',
401 | close: ')',
402 | },
403 | {
404 | open: "'",
405 | close: "'",
406 | notIn: ['string', 'comment'],
407 | },
408 | {
409 | open: '"',
410 | close: '"',
411 | notIn: ['string'],
412 | },
413 | {
414 | open: '`',
415 | close: '`',
416 | notIn: ['string', 'comment'],
417 | },
418 | {
419 | open: '/**',
420 | close: ' */',
421 | notIn: ['string'],
422 | },
423 | ],
424 | surroundingPairs: [
425 | {
426 | open: "'",
427 | close: "'",
428 | },
429 | {
430 | open: '"',
431 | close: '"',
432 | },
433 | {
434 | open: '{',
435 | close: '}',
436 | },
437 | {
438 | open: '[',
439 | close: ']',
440 | },
441 | {
442 | open: '(',
443 | close: ')',
444 | },
445 | {
446 | open: '<',
447 | close: '>',
448 | },
449 | {
450 | open: '`',
451 | close: '`',
452 | },
453 | ],
454 | colorizedBracketPairs: [
455 | ['(', ')'],
456 | ['[', ']'],
457 | ['{', '}'],
458 | ['<', '>'],
459 | ],
460 | autoCloseBefore: ';:.,=}])>` \n\t',
461 | folding: {
462 | markers: {
463 | start: /^\s*\/\/\s*#?region\b/,
464 | end: /^\s*\/\/\s*#?endregion\b/,
465 | },
466 | },
467 | wordPattern:
468 | /(-?\d*\.\d\w*)|([^\`\~\@\!\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>/\?\s]+)/,
469 | indentationRules: {
470 | decreaseIndentPattern: /^((?!.*?\/\*).*\*\/)?\s*[\}\]].*$/,
471 | increaseIndentPattern:
472 | /^((?!\/\/).)*(\{([^}"'`/]*|(\t|[ ])*\/\/.*)|\([^)"'`/]*|\[[^\]"'`/]*)$/,
473 | unIndentedLinePattern:
474 | /^(\t|[ ])*[ ]\*[^/]*\*\/\s*$|^(\t|[ ])*[ ]\*\/\s*$|^(\t|[ ])*[ ]\*([ ]([^\*]|\*(?!\/))*)?$/,
475 | },
476 | onEnterRules: [
477 | {
478 | beforeText: /^\s*\/\*\*(?!\/)([^\*]|\*(?!\/))*$/,
479 | afterText: /^\s*\*\/$/,
480 | action: {
481 | indentAction: languages.IndentAction.IndentOutdent,
482 | appendText: ' * ',
483 | },
484 | },
485 | {
486 | beforeText: /^\s*\/\*\*(?!\/)([^\*]|\*(?!\/))*$/,
487 | action: {
488 | indentAction: languages.IndentAction.None,
489 | appendText: ' * ',
490 | },
491 | },
492 | {
493 | beforeText: /^(\t|[ ])*[ ]\*([ ]([^\*]|\*(?!\/))*)?$/,
494 | previousLineText: /(?=^(\s*(\/\*\*|\*)).*)(?=(?!(\s*\*\/)))/,
495 | action: {
496 | indentAction: languages.IndentAction.None,
497 | appendText: '* ',
498 | },
499 | },
500 | {
501 | beforeText: /^(\t|[ ])*[ ]\*\/\s*$/,
502 | action: {
503 | indentAction: languages.IndentAction.None,
504 | removeText: 1,
505 | },
506 | },
507 | {
508 | beforeText: /^(\t|[ ])*[ ]\*[^/]*\*\/\s*$/,
509 | action: {
510 | indentAction: languages.IndentAction.None,
511 | removeText: 1,
512 | },
513 | },
514 | {
515 | beforeText: /^\s*(\bcase\s.+:|\bdefault:)$/,
516 | afterText: /^(?!\s*(\bcase\b|\bdefault\b))/,
517 | action: {
518 | indentAction: languages.IndentAction.Indent,
519 | },
520 | },
521 | {
522 | previousLineText: /^\s*(((else ?)?if|for|while)\s*\(.*\)\s*|else\s*)$/,
523 | beforeText: /^\s+([^{i\s]|i(?!f\b))/,
524 | action: {
525 | indentAction: languages.IndentAction.Outdent,
526 | },
527 | },
528 | ],
529 | }
530 |
--------------------------------------------------------------------------------
/src/monaco/utils.ts:
--------------------------------------------------------------------------------
1 | import { type Uri, editor } from 'monaco-editor-core'
2 |
3 | export function getOrCreateModel(
4 | uri: Uri,
5 | lang: string | undefined,
6 | value: string,
7 | ) {
8 | const model = editor.getModel(uri)
9 | if (model) {
10 | model.setValue(value)
11 | return model
12 | }
13 | return editor.createModel(value, lang, uri)
14 | }
15 |
--------------------------------------------------------------------------------
/src/monaco/vue.worker.ts:
--------------------------------------------------------------------------------
1 | // @ts-expect-error
2 | import * as worker from 'monaco-editor-core/esm/vs/editor/editor.worker'
3 | import type * as monaco from 'monaco-editor-core'
4 | import {
5 | type LanguageServiceEnvironment,
6 | createTypeScriptWorkerLanguageService,
7 | } from '@volar/monaco/worker'
8 | import { createNpmFileSystem } from '@volar/jsdelivr'
9 | import {
10 | type VueCompilerOptions,
11 | getFullLanguageServicePlugins,
12 | createVueLanguagePlugin,
13 | resolveVueCompilerOptions,
14 | } from '@vue/language-service'
15 | import type { WorkerHost, WorkerMessage } from './env'
16 | import { URI } from 'vscode-uri'
17 |
18 | export interface CreateData {
19 | tsconfig: {
20 | compilerOptions?: import('typescript').CompilerOptions
21 | vueCompilerOptions?: Partial
22 | }
23 | dependencies: Record
24 | }
25 |
26 | let ts: typeof import('typescript')
27 | let locale: string | undefined
28 |
29 | self.onmessage = async (msg: MessageEvent) => {
30 | if (msg.data?.event === 'init') {
31 | locale = msg.data.tsLocale
32 | ts = await importTsFromCdn(msg.data.tsVersion)
33 | self.postMessage('inited')
34 | return
35 | }
36 |
37 | worker.initialize(
38 | (
39 | ctx: monaco.worker.IWorkerContext,
40 | { tsconfig, dependencies }: CreateData,
41 | ) => {
42 | const asFileName = (uri: URI) => uri.path
43 | const asUri = (fileName: string): URI => URI.file(fileName)
44 | const env: LanguageServiceEnvironment = {
45 | workspaceFolders: [URI.file('/')],
46 | locale,
47 | fs: createNpmFileSystem(
48 | (uri) => {
49 | if (uri.scheme === 'file') {
50 | if (uri.path === '/node_modules') {
51 | return ''
52 | } else if (uri.path.startsWith('/node_modules/')) {
53 | return uri.path.slice('/node_modules/'.length)
54 | }
55 | }
56 | },
57 | (pkgName) => dependencies[pkgName],
58 | (path, content) => {
59 | ctx.host.onFetchCdnFile(
60 | asUri('/node_modules/' + path).toString(),
61 | content,
62 | )
63 | },
64 | ),
65 | }
66 |
67 | const { options: compilerOptions } = ts.convertCompilerOptionsFromJson(
68 | tsconfig?.compilerOptions || {},
69 | '',
70 | )
71 | const vueCompilerOptions = resolveVueCompilerOptions(
72 | tsconfig.vueCompilerOptions || {},
73 | )
74 |
75 | return createTypeScriptWorkerLanguageService({
76 | typescript: ts,
77 | compilerOptions,
78 | workerContext: ctx,
79 | env,
80 | uriConverter: {
81 | asFileName,
82 | asUri,
83 | },
84 | languagePlugins: [
85 | createVueLanguagePlugin(
86 | ts,
87 | compilerOptions,
88 | vueCompilerOptions,
89 | asFileName,
90 | ),
91 | ],
92 | languageServicePlugins: getFullLanguageServicePlugins(ts),
93 | setup({ project }) {
94 | project.vue = { compilerOptions: vueCompilerOptions }
95 | },
96 | })
97 | },
98 | )
99 | }
100 |
101 | async function importTsFromCdn(tsVersion: string) {
102 | const _module = globalThis.module
103 | ;(globalThis as any).module = { exports: {} }
104 | const tsUrl = `https://cdn.jsdelivr.net/npm/typescript@${tsVersion}/lib/typescript.js`
105 | await import(/* @vite-ignore */ tsUrl)
106 | const ts = globalThis.module.exports
107 | globalThis.module = _module
108 | return ts as typeof import('typescript')
109 | }
110 |
--------------------------------------------------------------------------------
/src/output/Output.vue:
--------------------------------------------------------------------------------
1 |
58 |
59 |
60 |
61 |
69 |
70 |
71 |
86 |
87 |
88 |
91 |
92 |
93 |
94 |
131 |
--------------------------------------------------------------------------------
/src/output/Preview.vue:
--------------------------------------------------------------------------------
1 |
23 |
24 |
25 |
34 |
35 |
--------------------------------------------------------------------------------
/src/output/PreviewProxy.ts:
--------------------------------------------------------------------------------
1 | // ReplProxy and srcdoc implementation from Svelte REPL
2 | // MIT License https://github.com/sveltejs/svelte-repl/blob/master/LICENSE
3 |
4 | let uid = 1
5 |
6 | export class PreviewProxy {
7 | iframe: HTMLIFrameElement
8 | handlers: Record
9 | pending_cmds: Map<
10 | number,
11 | { resolve: (value: unknown) => void; reject: (reason?: any) => void }
12 | >
13 | handle_event: (e: any) => void
14 |
15 | constructor(iframe: HTMLIFrameElement, handlers: Record) {
16 | this.iframe = iframe
17 | this.handlers = handlers
18 |
19 | this.pending_cmds = new Map()
20 |
21 | this.handle_event = (e) => this.handle_repl_message(e)
22 | window.addEventListener('message', this.handle_event, false)
23 | }
24 |
25 | destroy() {
26 | window.removeEventListener('message', this.handle_event)
27 | }
28 |
29 | iframe_command(action: string, args: any) {
30 | return new Promise((resolve, reject) => {
31 | const cmd_id = uid++
32 |
33 | this.pending_cmds.set(cmd_id, { resolve, reject })
34 |
35 | this.iframe.contentWindow!.postMessage({ action, cmd_id, args }, '*')
36 | })
37 | }
38 |
39 | handle_command_message(cmd_data: any) {
40 | let action = cmd_data.action
41 | let id = cmd_data.cmd_id
42 | let handler = this.pending_cmds.get(id)
43 |
44 | if (handler) {
45 | this.pending_cmds.delete(id)
46 | if (action === 'cmd_error') {
47 | let { message, stack } = cmd_data
48 | let e = new Error(message)
49 | e.stack = stack
50 | handler.reject(e)
51 | }
52 |
53 | if (action === 'cmd_ok') {
54 | handler.resolve(cmd_data.args)
55 | }
56 | } else if (action !== 'cmd_error' && action !== 'cmd_ok') {
57 | console.error('command not found', id, cmd_data, [
58 | ...this.pending_cmds.keys(),
59 | ])
60 | }
61 | }
62 |
63 | handle_repl_message(event: any) {
64 | if (event.source !== this.iframe.contentWindow) return
65 |
66 | const { action, args } = event.data
67 |
68 | switch (action) {
69 | case 'cmd_error':
70 | case 'cmd_ok':
71 | return this.handle_command_message(event.data)
72 | case 'fetch_progress':
73 | return this.handlers.on_fetch_progress(args.remaining)
74 | case 'error':
75 | return this.handlers.on_error(event.data)
76 | case 'unhandledrejection':
77 | return this.handlers.on_unhandled_rejection(event.data)
78 | case 'console':
79 | return this.handlers.on_console(event.data)
80 | case 'console_group':
81 | return this.handlers.on_console_group(event.data)
82 | case 'console_group_collapsed':
83 | return this.handlers.on_console_group_collapsed(event.data)
84 | case 'console_group_end':
85 | return this.handlers.on_console_group_end(event.data)
86 | }
87 | }
88 |
89 | eval(script: string | string[]) {
90 | return this.iframe_command('eval', { script })
91 | }
92 |
93 | handle_links() {
94 | return this.iframe_command('catch_clicks', {})
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/src/output/Sandbox.vue:
--------------------------------------------------------------------------------
1 |
340 |
341 |
342 |
348 |
349 |
353 |
354 |
355 |
367 |
--------------------------------------------------------------------------------
/src/output/SsrOutput.vue:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
10 |
HTML
11 |
{{ html }}
12 |
Context
13 |
{{ context }}
14 |
15 |
16 |
17 |
33 |
--------------------------------------------------------------------------------
/src/output/moduleCompiler.ts:
--------------------------------------------------------------------------------
1 | import type { File, Store } from '../store'
2 | import {
3 | MagicString,
4 | babelParse,
5 | extractIdentifiers,
6 | isInDestructureAssignment,
7 | isStaticProperty,
8 | walk,
9 | walkIdentifiers,
10 | } from 'vue/compiler-sfc'
11 | import type { ExportSpecifier, Identifier, Node } from '@babel/types'
12 |
13 | export function compileModulesForPreview(store: Store, isSSR = false) {
14 | const seen = new Set()
15 | const processed: string[] = []
16 | processFile(store, store.files[store.mainFile], processed, seen, isSSR)
17 |
18 | if (!isSSR) {
19 | // also add css files that are not imported
20 | for (const filename in store.files) {
21 | if (filename.endsWith('.css')) {
22 | const file = store.files[filename]
23 | if (!seen.has(file)) {
24 | processed.push(
25 | `\nwindow.__css__.push(${JSON.stringify(file.compiled.css)})`,
26 | )
27 | }
28 | }
29 | }
30 | }
31 |
32 | return processed
33 | }
34 |
35 | const modulesKey = `__modules__`
36 | const exportKey = `__export__`
37 | const dynamicImportKey = `__dynamic_import__`
38 | const moduleKey = `__module__`
39 |
40 | // similar logic with Vite's SSR transform, except this is targeting the browser
41 | function processFile(
42 | store: Store,
43 | file: File,
44 | processed: string[],
45 | seen: Set,
46 | isSSR: boolean,
47 | ) {
48 | if (seen.has(file)) {
49 | return []
50 | }
51 | seen.add(file)
52 |
53 | if (!isSSR && file.filename.endsWith('.html')) {
54 | return processHtmlFile(store, file.code, file.filename, processed, seen)
55 | }
56 |
57 | let {
58 | code: js,
59 | importedFiles,
60 | hasDynamicImport,
61 | } = processModule(
62 | store,
63 | isSSR ? file.compiled.ssr : file.compiled.js,
64 | file.filename,
65 | )
66 | processChildFiles(
67 | store,
68 | importedFiles,
69 | hasDynamicImport,
70 | processed,
71 | seen,
72 | isSSR,
73 | )
74 | // append css
75 | if (file.compiled.css && !isSSR) {
76 | js += `\nwindow.__css__.push(${JSON.stringify(file.compiled.css)})`
77 | }
78 |
79 | // push self
80 | processed.push(js)
81 | }
82 |
83 | function processChildFiles(
84 | store: Store,
85 | importedFiles: Set,
86 | hasDynamicImport: boolean,
87 | processed: string[],
88 | seen: Set,
89 | isSSR: boolean,
90 | ) {
91 | if (hasDynamicImport) {
92 | // process all files
93 | for (const file of Object.values(store.files)) {
94 | if (seen.has(file)) continue
95 | processFile(store, file, processed, seen, isSSR)
96 | }
97 | } else if (importedFiles.size > 0) {
98 | // crawl child imports
99 | for (const imported of importedFiles) {
100 | processFile(store, store.files[imported], processed, seen, isSSR)
101 | }
102 | }
103 | }
104 |
105 | function processModule(store: Store, src: string, filename: string) {
106 | const s = new MagicString(src)
107 |
108 | const ast = babelParse(src, {
109 | sourceFilename: filename,
110 | sourceType: 'module',
111 | }).program.body
112 |
113 | const idToImportMap = new Map()
114 | const declaredConst = new Set()
115 | const importedFiles = new Set()
116 | const importToIdMap = new Map()
117 |
118 | function resolveImport(raw: string): string | undefined {
119 | const files = store.files
120 | let resolved = raw
121 | const file =
122 | files[resolved] ||
123 | files[(resolved = raw + '.ts')] ||
124 | files[(resolved = raw + '.js')]
125 | return file ? resolved : undefined
126 | }
127 |
128 | function defineImport(node: Node, source: string) {
129 | const filename = resolveImport(source.replace(/^\.\/+/, 'src/'))
130 | if (!filename) {
131 | throw new Error(`File "${source}" does not exist.`)
132 | }
133 | if (importedFiles.has(filename)) {
134 | return importToIdMap.get(filename)!
135 | }
136 | importedFiles.add(filename)
137 | const id = `__import_${importedFiles.size}__`
138 | importToIdMap.set(filename, id)
139 | s.appendLeft(
140 | node.start!,
141 | `const ${id} = ${modulesKey}[${JSON.stringify(filename)}]\n`,
142 | )
143 | return id
144 | }
145 |
146 | function defineExport(name: string, local = name) {
147 | s.append(`\n${exportKey}(${moduleKey}, "${name}", () => ${local})`)
148 | }
149 |
150 | // 0. instantiate module
151 | s.prepend(
152 | `const ${moduleKey} = ${modulesKey}[${JSON.stringify(
153 | filename,
154 | )}] = { [Symbol.toStringTag]: "Module" }\n\n`,
155 | )
156 |
157 | // 1. check all import statements and record id -> importName map
158 | for (const node of ast) {
159 | // import foo from 'foo' --> foo -> __import_foo__.default
160 | // import { baz } from 'foo' --> baz -> __import_foo__.baz
161 | // import * as ok from 'foo' --> ok -> __import_foo__
162 | if (node.type === 'ImportDeclaration') {
163 | const source = node.source.value
164 | if (source.startsWith('./')) {
165 | const importId = defineImport(node, node.source.value)
166 | for (const spec of node.specifiers) {
167 | if (spec.type === 'ImportSpecifier') {
168 | idToImportMap.set(
169 | spec.local.name,
170 | `${importId}.${(spec.imported as Identifier).name}`,
171 | )
172 | } else if (spec.type === 'ImportDefaultSpecifier') {
173 | idToImportMap.set(spec.local.name, `${importId}.default`)
174 | } else {
175 | // namespace specifier
176 | idToImportMap.set(spec.local.name, importId)
177 | }
178 | }
179 | s.remove(node.start!, node.end!)
180 | }
181 | }
182 | }
183 |
184 | // 2. check all export statements and define exports
185 | for (const node of ast) {
186 | // named exports
187 | if (node.type === 'ExportNamedDeclaration') {
188 | if (node.declaration) {
189 | if (
190 | node.declaration.type === 'FunctionDeclaration' ||
191 | node.declaration.type === 'ClassDeclaration'
192 | ) {
193 | // export function foo() {}
194 | defineExport(node.declaration.id!.name)
195 | } else if (node.declaration.type === 'VariableDeclaration') {
196 | // export const foo = 1, bar = 2
197 | for (const decl of node.declaration.declarations) {
198 | for (const id of extractIdentifiers(decl.id)) {
199 | defineExport(id.name)
200 | }
201 | }
202 | }
203 | s.remove(node.start!, node.declaration.start!)
204 | } else if (node.source) {
205 | // export { foo, bar } from './foo'
206 | const importId = defineImport(node, node.source.value)
207 | for (const spec of node.specifiers) {
208 | defineExport(
209 | (spec.exported as Identifier).name,
210 | `${importId}.${(spec as ExportSpecifier).local.name}`,
211 | )
212 | }
213 | s.remove(node.start!, node.end!)
214 | } else {
215 | // export { foo, bar }
216 | for (const spec of node.specifiers) {
217 | const local = (spec as ExportSpecifier).local.name
218 | const binding = idToImportMap.get(local)
219 | defineExport((spec.exported as Identifier).name, binding || local)
220 | }
221 | s.remove(node.start!, node.end!)
222 | }
223 | }
224 |
225 | // default export
226 | if (node.type === 'ExportDefaultDeclaration') {
227 | if ('id' in node.declaration && node.declaration.id) {
228 | // named hoistable/class exports
229 | // export default function foo() {}
230 | // export default class A {}
231 | const { name } = node.declaration.id
232 | s.remove(node.start!, node.start! + 15)
233 | s.append(`\n${exportKey}(${moduleKey}, "default", () => ${name})`)
234 | } else {
235 | // anonymous default exports
236 | s.overwrite(node.start!, node.start! + 14, `${moduleKey}.default =`)
237 | }
238 | }
239 |
240 | // export * from './foo'
241 | if (node.type === 'ExportAllDeclaration') {
242 | const importId = defineImport(node, node.source.value)
243 | s.remove(node.start!, node.end!)
244 | s.append(`\nfor (const key in ${importId}) {
245 | if (key !== 'default') {
246 | ${exportKey}(${moduleKey}, key, () => ${importId}[key])
247 | }
248 | }`)
249 | }
250 | }
251 |
252 | // 3. convert references to import bindings
253 | for (const node of ast) {
254 | if (node.type === 'ImportDeclaration') continue
255 | walkIdentifiers(node, (id, parent, parentStack) => {
256 | const binding = idToImportMap.get(id.name)
257 | if (!binding) {
258 | return
259 | }
260 | if (parent && isStaticProperty(parent) && parent.shorthand) {
261 | // let binding used in a property shorthand
262 | // { foo } -> { foo: __import_x__.foo }
263 | // skip for destructure patterns
264 | if (
265 | !(parent as any).inPattern ||
266 | isInDestructureAssignment(parent, parentStack)
267 | ) {
268 | s.appendLeft(id.end!, `: ${binding}`)
269 | }
270 | } else if (
271 | parent &&
272 | parent.type === 'ClassDeclaration' &&
273 | id === parent.superClass
274 | ) {
275 | if (!declaredConst.has(id.name)) {
276 | declaredConst.add(id.name)
277 | // locate the top-most node containing the class declaration
278 | const topNode = parentStack[1]
279 | s.prependRight(topNode.start!, `const ${id.name} = ${binding};\n`)
280 | }
281 | } else {
282 | s.overwrite(id.start!, id.end!, binding)
283 | }
284 | })
285 | }
286 |
287 | // 4. convert dynamic imports
288 | let hasDynamicImport = false
289 | walk(ast, {
290 | enter(node: Node, parent: Node) {
291 | if (node.type === 'Import' && parent.type === 'CallExpression') {
292 | const arg = parent.arguments[0]
293 | if (arg.type === 'StringLiteral' && arg.value.startsWith('./')) {
294 | hasDynamicImport = true
295 | s.overwrite(node.start!, node.start! + 6, dynamicImportKey)
296 | s.overwrite(
297 | arg.start!,
298 | arg.end!,
299 | JSON.stringify(arg.value.replace(/^\.\/+/, 'src/')),
300 | )
301 | }
302 | }
303 | },
304 | })
305 |
306 | return {
307 | code: s.toString(),
308 | importedFiles,
309 | hasDynamicImport,
310 | }
311 | }
312 |
313 | const scriptRE = /
367 |
368 |
369 |
373 |
376 |
377 |
378 |
379 |
380 |