├── .changeset
├── README.md
├── config.json
├── red-geckos-reflect.md
├── six-penguins-accept.md
└── young-lies-allow.md
├── .eslintignore
├── .eslintrc.js
├── .github
├── CONTRIBUTING.md
├── FUNDING.yml
├── workflows-source
│ └── ci.yaml
└── workflows
│ ├── ci.yaml
│ └── release.yaml
├── .gitignore
├── .husky
├── .gitignore
└── pre-commit
├── .npmrc
├── .prettierrc.json
├── .vscode
├── launch.json
└── settings.json
├── LICENSE
├── docs
├── .vitepress
│ ├── .gitignore
│ └── config.js
├── assets
│ └── show-preview.svg
├── index.md
├── introduction.md
├── package.json
└── public
│ ├── android-chrome-192x192.png
│ ├── android-chrome-512x512.png
│ ├── apple-touch-icon-precomposed.png
│ ├── apple-touch-icon.png
│ ├── browserconfig.xml
│ ├── favicon-16x16.png
│ ├── favicon-32x32.png
│ ├── favicon.ico
│ ├── logo.png
│ ├── logo.svg
│ ├── mstile-144x144.png
│ ├── mstile-150x150.png
│ ├── mstile-310x150.png
│ ├── mstile-310x310.png
│ ├── mstile-70x70.png
│ ├── safari-pinned-tab.svg
│ └── site.webmanifest
├── example
├── babel.config.js
├── jest.config.js
├── jsconfig.json
├── package.json
├── postcss.config.js
├── preview.ts
├── src
│ ├── Card.vue
│ ├── Input.vue
│ ├── RepoList.spec.js
│ ├── RepoList.vue
│ └── style.css
└── vite.config.js
├── extension
├── .gitignore
├── .npmignore
├── .vscodeignore
├── CHANGELOG.md
├── LICENSE
├── icon.svg
├── logo.png
├── package.json
├── readme.md
├── scripts
│ └── build.mjs
├── src
│ ├── getWebviewContent.ts
│ ├── global.d.ts
│ └── index.ts
└── tsconfig.json
├── jest.config.js
├── lint-staged.config.js
├── package.json
├── packages
├── preview-compiler
│ ├── CHANGELOG.md
│ ├── package.json
│ ├── src
│ │ └── index.ts
│ └── tsconfig.json
├── preview-provider
│ ├── .npmignore
│ ├── CHANGELOG.md
│ ├── package.json
│ ├── src
│ │ ├── activeComponent.ts
│ │ ├── communication.ts
│ │ ├── components.ts
│ │ ├── fetch.ts
│ │ ├── index.ts
│ │ └── utilities.ts
│ └── tsconfig.json
├── preview-shell
│ ├── CHANGELOG.md
│ ├── index.html
│ ├── package.json
│ ├── postcss.config.js
│ ├── public
│ │ ├── browserconfig.xml
│ │ ├── favicon.ico
│ │ ├── icons
│ │ │ ├── android-icon-144x144.png
│ │ │ ├── android-icon-192x192.png
│ │ │ ├── android-icon-36x36.png
│ │ │ ├── android-icon-48x48.png
│ │ │ ├── android-icon-72x72.png
│ │ │ ├── android-icon-96x96.png
│ │ │ ├── apple-icon-114x114.png
│ │ │ ├── apple-icon-120x120.png
│ │ │ ├── apple-icon-144x144.png
│ │ │ ├── apple-icon-152x152.png
│ │ │ ├── apple-icon-180x180.png
│ │ │ ├── apple-icon-57x57.png
│ │ │ ├── apple-icon-60x60.png
│ │ │ ├── apple-icon-72x72.png
│ │ │ ├── apple-icon-76x76.png
│ │ │ ├── apple-icon-precomposed.png
│ │ │ ├── apple-icon.png
│ │ │ ├── favicon-16x16.png
│ │ │ ├── favicon-32x32.png
│ │ │ ├── favicon-96x96.png
│ │ │ ├── ms-icon-144x144.png
│ │ │ ├── ms-icon-150x150.png
│ │ │ ├── ms-icon-310x310.png
│ │ │ └── ms-icon-70x70.png
│ │ └── manifest.json
│ ├── src
│ │ ├── Root.vue
│ │ ├── app.css
│ │ ├── app.ts
│ │ ├── assets
│ │ │ ├── MacBook-Pro-16.png
│ │ │ ├── assets.d.ts
│ │ │ ├── device-freeform.svg
│ │ │ ├── iPad-Pro-13-Landscape.png
│ │ │ ├── iPad-Pro-13-Landscape.svg
│ │ │ ├── iPad-Pro-13-Portrait.png
│ │ │ ├── iPad-Pro-13-Portrait.svg
│ │ │ ├── iPhone-11-Landscape.png
│ │ │ ├── iPhone-11-Landscape.svg
│ │ │ ├── iPhone-11-Portrait.png
│ │ │ └── iPhone-11-Portrait.svg
│ │ ├── components.ts
│ │ ├── components
│ │ │ ├── BaseDevice.vue
│ │ │ ├── Browser.vue
│ │ │ ├── ConfiguredDevice.vue
│ │ │ ├── Content.vue
│ │ │ ├── Device.vue
│ │ │ ├── DeviceSelector.vue
│ │ │ ├── Example.vue
│ │ │ ├── ExplorerComponents.vue
│ │ │ ├── FreeformDevice.vue
│ │ │ ├── Viewport.vue
│ │ │ ├── ZoomSelector.vue
│ │ │ └── device-frame-hack.vue
│ │ ├── config.ts
│ │ ├── devices
│ │ │ └── index.ts
│ │ ├── index.ts
│ │ ├── pages
│ │ │ ├── dashboard.vue
│ │ │ └── sandbox.vue
│ │ ├── router.ts
│ │ └── utilities.ts
│ ├── tailwind.config.js
│ ├── tsconfig.json
│ └── vite.config.js
├── preview-test-utils
│ ├── .npmignore
│ ├── CHANGELOG.md
│ ├── jest.config.js
│ ├── package.json
│ ├── placeholder.js
│ ├── src
│ │ ├── index.ts
│ │ ├── stacktrace.ts
│ │ ├── transform.ts
│ │ └── vm.ts
│ ├── test
│ │ ├── fixtures
│ │ │ ├── Example.vue
│ │ │ ├── MockComponent.vue
│ │ │ ├── MockFetchRequest.vue
│ │ │ └── api
│ │ │ │ └── foo.json
│ │ ├── setup.ts
│ │ ├── tsconfig.json
│ │ └── usePreview.spec.ts
│ └── tsconfig.json
└── preview
│ ├── CHANGELOG.md
│ ├── bin
│ └── preview.js
│ ├── package.json
│ ├── src
│ ├── cli.ts
│ ├── generators.ts
│ ├── index.ts
│ ├── send.ts
│ ├── store
│ │ ├── ComponentMetadataStore.ts
│ │ ├── DescriptorStore.ts
│ │ ├── FileSystemHost.ts
│ │ └── PreviewCompilerStore.ts
│ ├── utils.ts
│ └── virtual-resource.ts
│ └── tsconfig.json
├── pnpm-lock.yaml
├── pnpm-workspace.yaml
├── readme.md
├── rollup.config.js
├── scripts
└── prepare-workflows.mjs
└── tsconfig.base.json
/.changeset/README.md:
--------------------------------------------------------------------------------
1 | # Changesets
2 |
3 | Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
4 | with multi-package repos, or single-package repos to help you version and publish your code. You can
5 | find the full documentation for it [in our repository](https://github.com/changesets/changesets)
6 |
7 | We have a quick list of common questions to get you started engaging with this project in
8 | [our documentation](https://github.com/changesets/changesets/blob/master/docs/common-questions.md)
9 |
--------------------------------------------------------------------------------
/.changeset/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://unpkg.com/@changesets/config@1.4.0/schema.json",
3 | "changelog": "@changesets/cli/changelog",
4 | "commit": false,
5 | "linked": [],
6 | "access": "public",
7 | "baseBranch": "main",
8 | "updateInternalDependencies": "patch"
9 | }
10 |
--------------------------------------------------------------------------------
/.changeset/red-geckos-reflect.md:
--------------------------------------------------------------------------------
1 | ---
2 | '@vuedx/preview-provider': patch
3 | ---
4 |
5 | fetch stub for delayed response
6 |
7 | - `$p.http.delayed()`
8 |
--------------------------------------------------------------------------------
/.changeset/six-penguins-accept.md:
--------------------------------------------------------------------------------
1 | ---
2 | '@vuedx/preview-shell': patch
3 | ---
4 |
5 | Global overrides for device and zoom
6 |
--------------------------------------------------------------------------------
/.changeset/young-lies-allow.md:
--------------------------------------------------------------------------------
1 | ---
2 | '@vuedx/preview-shell': patch
3 | ---
4 |
5 | Filter components
6 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | packages/example
2 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | extends: '@vuedx/eslint-config',
4 | };
5 |
--------------------------------------------------------------------------------
/.github/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | > This document is WIP. If you have any questions, please create an issue.
4 |
5 | ## Development
6 |
7 | ```bash
8 | # install dependencies
9 | pnpm install
10 | # start rollup dev build watcher
11 | pnpm run watch
12 | # start preview in `packages/example` directory
13 | pnpm start
14 | ```
15 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: znck
2 |
--------------------------------------------------------------------------------
/.github/workflows-source/ci.yaml:
--------------------------------------------------------------------------------
1 | name: CI/CD
2 |
3 | on:
4 | pull_request:
5 | branches:
6 | - main
7 | push:
8 | branches:
9 | - main
10 |
11 | .checkout: &checkout
12 | - name: Checkout
13 | uses: actions/checkout@v2
14 |
15 | .node_modules_cache: &node_modules_cache
16 | - name: Cache node packages
17 | uses: actions/cache@v2
18 | env:
19 | cache-name: pnpm-modules
20 | with:
21 | key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/pnpm-lock.yaml') }}
22 | restore-keys: |
23 | ${{ runner.os }}-build-${{ env.cache-name }}-
24 | ${{ runner.os }}-build-
25 | ${{ runner.os }}-
26 | path: |
27 | ~/.pnpm-store
28 | ${{ github.workspace }}/.pnpm
29 | .node: &node
30 | - name: Setup Node
31 | uses: actions/setup-node@v2
32 | with:
33 | node-version: '16'
34 |
35 | .pnpm: &pnpm
36 | - name: Setup PNPM
37 | uses: pnpm/action-setup@v2.0.1
38 | with:
39 | version: '6.2.5'
40 | run_install: |
41 | - recursive: true
42 | args: [--frozen-lockfile]
43 |
44 | .setup: &setup
45 | - *checkout
46 | - *node_modules_cache
47 | - *node
48 | - *pnpm
49 |
50 | .download: &download
51 | - name: Download build artefact
52 | uses: actions/download-artifact@v2
53 | with:
54 | name: build-artefact
55 | path: '.'
56 |
57 | concurrency:
58 | group: build-${{ github.ref }}
59 | cancel-in-progress: ${{ !startsWith(github.event.head_commit.message, 'release:') }}
60 |
61 | jobs:
62 | build:
63 | name: Build
64 | runs-on: ubuntu-latest
65 | if: "!startsWith(github.event.head_commit.message, 'skip ci')"
66 | steps:
67 | - *setup
68 | - name: Build
69 | run: pnpm recursive run build
70 | - name: Upload build artefact
71 | uses: actions/upload-artifact@v2
72 | with:
73 | name: build-artefact
74 | retention-days: 30
75 | path: |
76 | ./packages/*/dist
77 | ./extension/dist
78 |
79 | unit:
80 | needs: build
81 | strategy:
82 | matrix:
83 | os: [ubuntu-latest]
84 | node_version: ['14', '16']
85 | include:
86 | - os: ubuntu-latest
87 | node: '16'
88 | command: 'coverage'
89 | runs-on: ${{ matrix.os }}
90 | name: Node ${{ matrix.node_version }} on ${{ matrix.os }}
91 | continue-on-error: ${{ startsWith(github.event.head_commit.message, 'release:') }}
92 | steps:
93 | - *checkout
94 | - *node_modules_cache
95 | - name: Setup Node
96 | uses: actions/setup-node@v2
97 | with:
98 | node-version: ${{ matrix.node_version }}
99 | - *pnpm
100 | - *download
101 | - name: Test
102 | run: pnpm test
103 | - name: Collect coverage
104 | if: matrix.command == 'coverage'
105 | uses: codecov/codecov-action@v1
106 | with:
107 | file: ./coverage/coverage-final.json
108 |
109 | pre-release:
110 | name: Pre-release
111 | runs-on: ubuntu-latest
112 | needs: unit
113 | concurrency:
114 | group: pre-release
115 | cancel-in-progress: true
116 | steps:
117 | - *setup
118 | - *download
119 | - name: Publish Pre-release Extension
120 | run: |
121 | pnpm recursive --filter ./extension run build
122 | pnpm recursive --filter ./extension run pre-release
123 | env:
124 | RELEASE_CHANNEL: pre-release
125 | VSCODE_MARKETPLACE_TOKEN: ${{ secrets.VSCODE_MARKETPLACE_TOKEN }}
126 | - name: Publish Pre-release Packages
127 | run: |
128 | pnpm recursive --filter ./packages exec -- pnpm version prerelease --no-commit-hooks --no-git-tag-version --preid=next-$(date +%s)
129 | echo '//registry.npmjs.org/:_authToken=${NPM_TOKEN}' > .npmrc
130 | pnpm recursive --filter ./packages publish --tag next --access public --no-git-checks
131 | env:
132 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
133 |
134 | check_release:
135 | name: Check Release
136 | runs-on: ubuntu-latest
137 | needs: unit
138 | if: startsWith(github.event.head_commit.message, 'release:')
139 | steps:
140 | - run: echo 'ok'
141 |
142 | release:
143 | name: Release
144 | runs-on: ubuntu-latest
145 | environment:
146 | name: Production
147 | url: https://marketplace.visualstudio.com/items?itemName=znck.preview
148 | needs: check_release
149 | concurrency:
150 | group: release
151 | cancel-in-progress: false
152 | steps:
153 | - *setup
154 | - *download
155 | - name: Publish Packages
156 | run: |
157 | echo '//registry.npmjs.org/:_authToken=${NPM_TOKEN}' > .npmrc
158 | pnpm recursive publish --no-git-checks
159 | env:
160 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
161 | - name: Publish Extension
162 | run: |
163 | pnpm recursive --filter ./extension run build
164 | pnpm recursive --filter ./extension run release
165 | pnpm -y osvx publish -p ${OVSX_REGISTRY_TOKEN} ./extension/preview.vsix
166 | env:
167 | VSCODE_MARKETPLACE_TOKEN: ${{ secrets.VSCODE_MARKETPLACE_TOKEN }}
168 | OVSX_REGISTRY_TOKEN: ${{ secrets.OVSX_REGISTRY_TOKEN }}
169 | continue-on-error: true
170 | - uses: 'marvinpinto/action-automatic-releases@latest'
171 | with:
172 | repo_token: '${{ secrets.GITHUB_TOKEN }}'
173 | automatic_release_tag: 'latest'
174 | prerelease: false
175 | files: |
176 | extension/CHANGELOG.md
177 | extension/preview.vsix
178 | packages/*/CHANGELOG.md
179 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yaml:
--------------------------------------------------------------------------------
1 | # THIS FILE WAS AUTOMATICALLY GENERATED (DO NOT MODIFY) from ../workflows-source/ci.yaml
2 | name: CI/CD
3 | 'on':
4 | pull_request:
5 | branches:
6 | - main
7 | push:
8 | branches:
9 | - main
10 | concurrency:
11 | group: build-${{ github.ref }}
12 | cancel-in-progress: ${{ !startsWith(github.event.head_commit.message, 'release:') }}
13 | jobs:
14 | build:
15 | name: Build
16 | runs-on: ubuntu-latest
17 | if: "!startsWith(github.event.head_commit.message, 'skip ci')"
18 | steps:
19 | - name: Checkout
20 | uses: actions/checkout@v2
21 | - name: Cache node packages
22 | uses: actions/cache@v2
23 | env:
24 | cache-name: pnpm-modules
25 | with:
26 | key: >-
27 | ${{ runner.os }}-build-${{ env.cache-name }}-${{
28 | hashFiles('**/pnpm-lock.yaml') }}
29 | restore-keys: |
30 | ${{ runner.os }}-build-${{ env.cache-name }}-
31 | ${{ runner.os }}-build-
32 | ${{ runner.os }}-
33 | path: |
34 | ~/.pnpm-store
35 | ${{ github.workspace }}/.pnpm
36 | - name: Setup Node
37 | uses: actions/setup-node@v2
38 | with:
39 | node-version: '16'
40 | - name: Setup PNPM
41 | uses: pnpm/action-setup@v2.0.1
42 | with:
43 | version: 6.2.5
44 | run_install: |
45 | - recursive: true
46 | args: [--frozen-lockfile]
47 | - name: Build
48 | run: pnpm recursive run build
49 | - name: Upload build artefact
50 | uses: actions/upload-artifact@v2
51 | with:
52 | name: build-artefact
53 | retention-days: 30
54 | path: |
55 | ./packages/*/dist
56 | ./extension/dist
57 | unit:
58 | needs: build
59 | strategy:
60 | matrix:
61 | os:
62 | - ubuntu-latest
63 | node_version:
64 | - '14'
65 | - '16'
66 | include:
67 | - os: ubuntu-latest
68 | node: '16'
69 | command: coverage
70 | runs-on: ${{ matrix.os }}
71 | name: Node ${{ matrix.node_version }} on ${{ matrix.os }}
72 | continue-on-error: ${{ startsWith(github.event.head_commit.message, 'release:') }}
73 | steps:
74 | - name: Checkout
75 | uses: actions/checkout@v2
76 | - name: Cache node packages
77 | uses: actions/cache@v2
78 | env:
79 | cache-name: pnpm-modules
80 | with:
81 | key: >-
82 | ${{ runner.os }}-build-${{ env.cache-name }}-${{
83 | hashFiles('**/pnpm-lock.yaml') }}
84 | restore-keys: |
85 | ${{ runner.os }}-build-${{ env.cache-name }}-
86 | ${{ runner.os }}-build-
87 | ${{ runner.os }}-
88 | path: |
89 | ~/.pnpm-store
90 | ${{ github.workspace }}/.pnpm
91 | - name: Setup Node
92 | uses: actions/setup-node@v2
93 | with:
94 | node-version: ${{ matrix.node_version }}
95 | - name: Setup PNPM
96 | uses: pnpm/action-setup@v2.0.1
97 | with:
98 | version: 6.2.5
99 | run_install: |
100 | - recursive: true
101 | args: [--frozen-lockfile]
102 | - name: Download build artefact
103 | uses: actions/download-artifact@v2
104 | with:
105 | name: build-artefact
106 | path: .
107 | - name: Test
108 | run: pnpm test
109 | - name: Collect coverage
110 | if: matrix.command == 'coverage'
111 | uses: codecov/codecov-action@v1
112 | with:
113 | file: ./coverage/coverage-final.json
114 | pre-release:
115 | name: Pre-release
116 | runs-on: ubuntu-latest
117 | needs: unit
118 | concurrency:
119 | group: pre-release
120 | cancel-in-progress: true
121 | steps:
122 | - name: Checkout
123 | uses: actions/checkout@v2
124 | - name: Cache node packages
125 | uses: actions/cache@v2
126 | env:
127 | cache-name: pnpm-modules
128 | with:
129 | key: >-
130 | ${{ runner.os }}-build-${{ env.cache-name }}-${{
131 | hashFiles('**/pnpm-lock.yaml') }}
132 | restore-keys: |
133 | ${{ runner.os }}-build-${{ env.cache-name }}-
134 | ${{ runner.os }}-build-
135 | ${{ runner.os }}-
136 | path: |
137 | ~/.pnpm-store
138 | ${{ github.workspace }}/.pnpm
139 | - name: Setup Node
140 | uses: actions/setup-node@v2
141 | with:
142 | node-version: '16'
143 | - name: Setup PNPM
144 | uses: pnpm/action-setup@v2.0.1
145 | with:
146 | version: 6.2.5
147 | run_install: |
148 | - recursive: true
149 | args: [--frozen-lockfile]
150 | - name: Download build artefact
151 | uses: actions/download-artifact@v2
152 | with:
153 | name: build-artefact
154 | path: .
155 | - name: Publish Pre-release Extension
156 | run: |
157 | pnpm recursive --filter ./extension run build
158 | pnpm recursive --filter ./extension run pre-release
159 | env:
160 | RELEASE_CHANNEL: pre-release
161 | VSCODE_MARKETPLACE_TOKEN: ${{ secrets.VSCODE_MARKETPLACE_TOKEN }}
162 | - name: Publish Pre-release Packages
163 | run: >
164 | pnpm recursive --filter ./packages exec -- pnpm version prerelease
165 | --no-commit-hooks --no-git-tag-version --preid=next-$(date +%s)
166 |
167 | echo '//registry.npmjs.org/:_authToken=${NPM_TOKEN}' > .npmrc
168 |
169 | pnpm recursive --filter ./packages publish --tag next --access public
170 | --no-git-checks
171 | env:
172 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
173 | check_release:
174 | name: Check Release
175 | runs-on: ubuntu-latest
176 | needs: unit
177 | if: startsWith(github.event.head_commit.message, 'release:')
178 | steps:
179 | - run: echo 'ok'
180 | release:
181 | name: Release
182 | runs-on: ubuntu-latest
183 | environment:
184 | name: Production
185 | url: https://marketplace.visualstudio.com/items?itemName=znck.preview
186 | needs: check_release
187 | concurrency:
188 | group: release
189 | cancel-in-progress: false
190 | steps:
191 | - name: Checkout
192 | uses: actions/checkout@v2
193 | - name: Cache node packages
194 | uses: actions/cache@v2
195 | env:
196 | cache-name: pnpm-modules
197 | with:
198 | key: >-
199 | ${{ runner.os }}-build-${{ env.cache-name }}-${{
200 | hashFiles('**/pnpm-lock.yaml') }}
201 | restore-keys: |
202 | ${{ runner.os }}-build-${{ env.cache-name }}-
203 | ${{ runner.os }}-build-
204 | ${{ runner.os }}-
205 | path: |
206 | ~/.pnpm-store
207 | ${{ github.workspace }}/.pnpm
208 | - name: Setup Node
209 | uses: actions/setup-node@v2
210 | with:
211 | node-version: '16'
212 | - name: Setup PNPM
213 | uses: pnpm/action-setup@v2.0.1
214 | with:
215 | version: 6.2.5
216 | run_install: |
217 | - recursive: true
218 | args: [--frozen-lockfile]
219 | - name: Download build artefact
220 | uses: actions/download-artifact@v2
221 | with:
222 | name: build-artefact
223 | path: .
224 | - name: Publish Packages
225 | run: |
226 | echo '//registry.npmjs.org/:_authToken=${NPM_TOKEN}' > .npmrc
227 | pnpm recursive publish --no-git-checks
228 | env:
229 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
230 | - name: Publish Extension
231 | run: >
232 | pnpm recursive --filter ./extension run build
233 |
234 | pnpm recursive --filter ./extension run release
235 |
236 | pnpm -y osvx publish -p ${OVSX_REGISTRY_TOKEN}
237 | ./extension/preview.vsix
238 | env:
239 | VSCODE_MARKETPLACE_TOKEN: ${{ secrets.VSCODE_MARKETPLACE_TOKEN }}
240 | OVSX_REGISTRY_TOKEN: ${{ secrets.OVSX_REGISTRY_TOKEN }}
241 | continue-on-error: true
242 | - uses: marvinpinto/action-automatic-releases@latest
243 | with:
244 | repo_token: ${{ secrets.GITHUB_TOKEN }}
245 | automatic_release_tag: latest
246 | prerelease: false
247 | files: |
248 | extension/CHANGELOG.md
249 | extension/preview.vsix
250 | packages/*/CHANGELOG.md
251 |
--------------------------------------------------------------------------------
/.github/workflows/release.yaml:
--------------------------------------------------------------------------------
1 | name: Release
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | jobs:
8 | create:
9 | name: Create release PR
10 | runs-on: ubuntu-latest
11 | steps:
12 | - name: Checkout
13 | uses: actions/checkout@v2
14 | with:
15 | fetch-depth: 0
16 |
17 | - name: Cache node packages
18 | uses: actions/cache@v2
19 | env:
20 | cache-name: pnpm-modules
21 | with:
22 | key: >-
23 | ${{ runner.os }}-build-${{ env.cache-name }}-${{
24 | hashFiles('**/pnpm-lock.yaml') }}
25 | restore-keys: |
26 | ${{ runner.os }}-build-${{ env.cache-name }}-
27 | ${{ runner.os }}-build-
28 | ${{ runner.os }}-
29 | path: |
30 | ~/.pnpm-store
31 | ${{ github.workspace }}/.pnpm
32 | - name: Setup Node
33 | uses: actions/setup-node@v2
34 | with:
35 | node-version: '16'
36 | - name: Setup PNPM
37 | uses: pnpm/action-setup@v2.0.1
38 | with:
39 | version: 6.2.5
40 | run_install: |
41 | - recursive: true
42 | args: [--frozen-lockfile]
43 |
44 | - name: Create Release Pull Request
45 | uses: changesets/action@master
46 | with:
47 | title: 'release: new versions'
48 | commit: 'release: new versions'
49 | env:
50 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
51 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | .eslintcache
4 | dist
5 | coverage
6 | _
7 | .pnpm/
8 |
--------------------------------------------------------------------------------
/.husky/.gitignore:
--------------------------------------------------------------------------------
1 | _
2 |
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | . "$(dirname "$0")/_/husky.sh"
3 |
4 | $(pnpm bin)/lint-staged
5 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | scope=@vuedx
2 | engine-strict = true
3 | virtual-store-dir = .pnpm
4 |
--------------------------------------------------------------------------------
/.prettierrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "semi": true,
3 | "singleQuote": true,
4 | "trailingComma": "es5",
5 | "printWidth": 100
6 | }
7 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "name": "Extension: VS Code",
9 | "type": "extensionHost",
10 | "request": "launch",
11 | "runtimeExecutable": "${execPath}",
12 | "env": {
13 | "TSS_DEBUG": "9999"
14 | },
15 | "args": [
16 | "--extensionDevelopmentPath=${workspaceRoot}/extension",
17 | "${workspaceRoot}/example"
18 | ],
19 | "outFiles": ["${workspaceRoot}/extension/dist/*.js"]
20 | }
21 | ]
22 | }
23 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "cSpell.words": [
3 | "codecov",
4 | "sirv"
5 | ]
6 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Rahul Kadyan
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/docs/.vitepress/.gitignore:
--------------------------------------------------------------------------------
1 | .cache/
2 | .temp/
3 |
--------------------------------------------------------------------------------
/docs/.vitepress/config.js:
--------------------------------------------------------------------------------
1 | // @ts-check
2 | module.exports = /** @type {import('vitepress').UserConfig} */ ({
3 | title: 'Preview',
4 | description: '',
5 | head: [
6 | ['link', { rel: 'apple-touch-icon', sizes: '180x180', href: '/apple-touch-icon.png' }],
7 | ['link', { rel: 'icon', type: 'image/png', sizes: '32x32', href: '/favicon-32x32.png' }],
8 | ['link', { rel: 'icon', type: 'image/png', sizes: '16x16', href: '/favicon-16x16.png' }],
9 | ['link', { rel: 'manifest', href: '/site.webmanifest' }],
10 | ['link', { rel: 'mask-icon', href: '/safari-pinned-tab.svg', color: '#ec3d3d' }],
11 | ['meta', { name: 'apple-mobile-web-app-title', content: 'VueDX Preview' }],
12 | ['meta', { name: 'application-name', content: 'VueDX Preview' }],
13 | ['meta', { name: 'msapplication-TileColor', content: '#b91d47' }],
14 | ['meta', { name: 'theme-color', content: '#ffffff' }],
15 | ],
16 | themeConfig: {
17 | locales: {},
18 | },
19 | });
20 |
--------------------------------------------------------------------------------
/docs/assets/show-preview.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/docs/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | home: true
3 | title: Preview | VueDX
4 | heroImage: /logo.png
5 | actionText: Get Started
6 | actionLink: /introduction.html
7 | features:
8 | - title: Sandbox
9 | details: Build components in isolated environment.
10 | - title: Visibility
11 | details: Render components in any states.
12 | - title:
13 | details:
14 | footer: MIT Licensed | Copyright © 2021-present Rahul Kadyan
15 | ---
16 |
--------------------------------------------------------------------------------
/docs/introduction.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vuedx/preview/d47255746d42a5a423037718ef8d8b62b5a692f0/docs/introduction.md
--------------------------------------------------------------------------------
/docs/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "name": "-docs",
4 | "scripts": {
5 | "dev": "vitepress"
6 | },
7 | "devDependencies": {
8 | "vitepress": "^0.16.1"
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/docs/public/android-chrome-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vuedx/preview/d47255746d42a5a423037718ef8d8b62b5a692f0/docs/public/android-chrome-192x192.png
--------------------------------------------------------------------------------
/docs/public/android-chrome-512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vuedx/preview/d47255746d42a5a423037718ef8d8b62b5a692f0/docs/public/android-chrome-512x512.png
--------------------------------------------------------------------------------
/docs/public/apple-touch-icon-precomposed.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vuedx/preview/d47255746d42a5a423037718ef8d8b62b5a692f0/docs/public/apple-touch-icon-precomposed.png
--------------------------------------------------------------------------------
/docs/public/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vuedx/preview/d47255746d42a5a423037718ef8d8b62b5a692f0/docs/public/apple-touch-icon.png
--------------------------------------------------------------------------------
/docs/public/browserconfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | #b91d47
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/docs/public/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vuedx/preview/d47255746d42a5a423037718ef8d8b62b5a692f0/docs/public/favicon-16x16.png
--------------------------------------------------------------------------------
/docs/public/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vuedx/preview/d47255746d42a5a423037718ef8d8b62b5a692f0/docs/public/favicon-32x32.png
--------------------------------------------------------------------------------
/docs/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vuedx/preview/d47255746d42a5a423037718ef8d8b62b5a692f0/docs/public/favicon.ico
--------------------------------------------------------------------------------
/docs/public/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vuedx/preview/d47255746d42a5a423037718ef8d8b62b5a692f0/docs/public/logo.png
--------------------------------------------------------------------------------
/docs/public/logo.svg:
--------------------------------------------------------------------------------
1 |
25 |
--------------------------------------------------------------------------------
/docs/public/mstile-144x144.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vuedx/preview/d47255746d42a5a423037718ef8d8b62b5a692f0/docs/public/mstile-144x144.png
--------------------------------------------------------------------------------
/docs/public/mstile-150x150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vuedx/preview/d47255746d42a5a423037718ef8d8b62b5a692f0/docs/public/mstile-150x150.png
--------------------------------------------------------------------------------
/docs/public/mstile-310x150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vuedx/preview/d47255746d42a5a423037718ef8d8b62b5a692f0/docs/public/mstile-310x150.png
--------------------------------------------------------------------------------
/docs/public/mstile-310x310.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vuedx/preview/d47255746d42a5a423037718ef8d8b62b5a692f0/docs/public/mstile-310x310.png
--------------------------------------------------------------------------------
/docs/public/mstile-70x70.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vuedx/preview/d47255746d42a5a423037718ef8d8b62b5a692f0/docs/public/mstile-70x70.png
--------------------------------------------------------------------------------
/docs/public/safari-pinned-tab.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
28 |
--------------------------------------------------------------------------------
/docs/public/site.webmanifest:
--------------------------------------------------------------------------------
1 | {
2 | "name": "VueDX Preview",
3 | "short_name": "VueDX Preview",
4 | "icons": [
5 | {
6 | "src": "/android-chrome-192x192.png",
7 | "sizes": "192x192",
8 | "type": "image/png"
9 | },
10 | {
11 | "src": "/android-chrome-512x512.png",
12 | "sizes": "512x512",
13 | "type": "image/png"
14 | }
15 | ],
16 | "theme_color": "#ffffff",
17 | "background_color": "#ffffff"
18 | }
19 |
--------------------------------------------------------------------------------
/example/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | [
4 | '@babel/preset-env',
5 | {
6 | targets: {
7 | node: 'current',
8 | },
9 | },
10 | ],
11 | ],
12 | };
13 |
--------------------------------------------------------------------------------
/example/jest.config.js:
--------------------------------------------------------------------------------
1 | const { name } = require('./package.json');
2 |
3 | /** @type {import('@jest/types').Config.InitialOptions} */
4 | module.exports = {
5 | displayName: {
6 | name,
7 | color: 'magenta',
8 | },
9 | verbose: true,
10 | moduleFileExtensions: ['js', 'json', 'vue'],
11 | transform: {
12 | '^.+\\.js$': 'babel-jest',
13 | '^.+\\.vue$': 'vue-jest',
14 | },
15 | transformIgnorePatterns: ['node_modules'],
16 | rootDir: __dirname,
17 | };
18 |
--------------------------------------------------------------------------------
/example/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.base.json",
3 | "include": ["src"],
4 | "compilerOptions": { "lib": ["ES2019", "DOM"], "checkJs": true }
5 | }
6 |
--------------------------------------------------------------------------------
/example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "name": "example",
4 | "version": "0.0.0",
5 | "buildConfig": {
6 | "useMain": false
7 | },
8 | "scripts": {
9 | "start": "node ../preview/bin/preview.js",
10 | "dev": "vite",
11 | "test": "jest"
12 | },
13 | "dependencies": {
14 | "vue": "^3.0.5"
15 | },
16 | "devDependencies": {
17 | "@babel/core": "^7.12.10",
18 | "@babel/preset-env": "^7.12.11",
19 | "@testing-library/dom": "^7.29.4",
20 | "@types/jest": "^26.0.20",
21 | "@vitejs/plugin-vue": "^1.1.1",
22 | "@vue/compiler-sfc": "^3.0.5",
23 | "@vue/test-utils": "2.0.0-beta.14",
24 | "@vuedx/preview-test-utils": "link:../packages/preview-test-utils",
25 | "babel-jest": "^26.6.3",
26 | "jest": "^26.6.3",
27 | "tailwindcss": "~2.0.2",
28 | "typescript": "^4.1.3",
29 | "vite": "^2.3.6",
30 | "vue-jest": "5.0.0-alpha.8",
31 | "whatwg-fetch": "^3.5.0"
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/example/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = { plugins: [] };
2 |
--------------------------------------------------------------------------------
/example/preview.ts:
--------------------------------------------------------------------------------
1 | import './src/style.css';
2 |
3 |
--------------------------------------------------------------------------------
/example/src/Card.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
--------------------------------------------------------------------------------
/example/src/Input.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
11 |
12 |
13 |
101 |
--------------------------------------------------------------------------------
/example/src/RepoList.spec.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-undef */
2 | import { waitFor } from '@testing-library/dom';
3 | import { mount } from '@vue/test-utils';
4 | import { usePreview } from '@vuedx/preview-test-utils';
5 | import 'whatwg-fetch';
6 |
7 | describe('RepoList', () => {
8 | test('should render list of repositories', async () => {
9 | const wrapper = mount(usePreview('one repo'));
10 | await waitFor(() => {
11 | expect(wrapper.findAll('li')).toHaveLength(1);
12 | expect(wrapper.find('li').text()).toBe('preview');
13 | });
14 | });
15 | });
16 |
--------------------------------------------------------------------------------
/example/src/RepoList.vue:
--------------------------------------------------------------------------------
1 |
18 |
19 |
20 |
21 | -
22 | {{ repo.name }}
23 |
24 |
25 |
26 |
27 |
28 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/example/src/style.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 |
3 | @tailwind components;
4 |
5 | @tailwind utilities;
6 |
--------------------------------------------------------------------------------
/example/vite.config.js:
--------------------------------------------------------------------------------
1 | const vuePlugin = require('@vitejs/plugin-vue').default;
2 | const { PreviewPlugin } = require('../packages/preview');
3 |
4 | module.exports = /** @type {import('vite').UserConfig} */ ({
5 | plugins: [vuePlugin(), PreviewPlugin()],
6 | clearScreen: false,
7 | server: {
8 | host: 'dev.thesemetrics.org',
9 | port: 2000,
10 | },
11 | });
12 |
--------------------------------------------------------------------------------
/extension/.gitignore:
--------------------------------------------------------------------------------
1 | package.bak.json
2 | *.vsix
3 |
--------------------------------------------------------------------------------
/extension/.npmignore:
--------------------------------------------------------------------------------
1 | /scripts
2 |
--------------------------------------------------------------------------------
/extension/.vscodeignore:
--------------------------------------------------------------------------------
1 | **
2 | !readme.md
3 | !logo.png
4 | !LICENSE
5 | !dist/
6 |
--------------------------------------------------------------------------------
/extension/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # preview
2 |
3 | ## 0.4.0
4 |
5 | ### Minor Changes
6 |
7 | - ff930e3: Use `/__preview` path prefix for plugin mode
8 |
9 | BREAKING CHANGE: Internal virtual files also use `__preview` prefix, instead of `@preview`.
10 |
11 | ## 0.3.2
12 |
13 | ### Patch Changes
14 |
15 | - cc16fc4: Ignore failures in metadata extraction from static code analysis
16 |
17 | ## 0.3.1
18 |
19 | ### Patch Changes
20 |
21 | - dd448e6: Sync color-scheme with VS Code
22 |
23 | ## 0.3.0
24 |
25 | ### Minor Changes
26 |
27 | - b0eea6a: New commands
28 |
29 | - `preview.showSource` — Open source .vue file from preview panel
30 | - `preview.update` — Update `@vuedx/preview` package
31 | - `preview.refresh` - Refresh preview panel
32 |
33 | - b0eea6a: Move "open preview in browser" to `editor/title/context` menu
34 |
35 | ### Patch Changes
36 |
37 | - b0eea6a: Build using @vuedx/monorepo-tools
38 |
39 | ## 0.2.4
40 |
41 | ### Patch Changes
42 |
43 | - 83e89e5: Use consistent virtual resource scheme
44 |
45 | - Use `@preview:component/${type}.${ext}` for component scoped resources
46 | - Use `@preview:shell/` for proxying pre-built shell application
47 | - Use `@preview:components.js` for component index
48 | - Use `@preview:user/setup.js` for custom runtime setup file
49 |
50 | ## 0.2.1
51 |
52 | ### Patch Changes
53 |
54 | - 1b39181: Bump all versions to test continuous release CI
55 |
56 | ## 0.2.0
57 |
58 | ### Minor Changes
59 |
60 | - 387878f: Open preview in browser
61 |
62 | - Add command to open preview in browser: `preview.open`
63 | - Add editor title menu entry for `preview.open`
64 |
65 | ## 0.1.5
66 |
67 | ### Patch Changes
68 |
69 | - 18a1b02: Bump version
70 |
71 | ## 0.1.4
72 |
73 | ### Patch Changes
74 |
75 | - e3ba132: Update HMR API
76 |
77 | - Version lock on vite@2.0.0-beta.44
78 | - Remove forked HMR client and use `/@vite/client` instead
79 |
80 | ## 0.1.3
81 |
82 | ### Patch Changes
83 |
84 | - 03225aa: Update vite HMR API
85 |
86 | ## 0.1.2
87 |
88 | ### Patch Changes
89 |
90 | - c6ed393: Automated release workflow
91 |
92 | ## 0.1.1
93 |
94 | ### Patch Changes
95 |
96 | - c2d20eb: Setup CI release workflow
97 |
98 | ## 0.1.0
99 |
100 | ### Minor Changes
101 |
102 | - d34a262: VS Code preview extension
103 |
--------------------------------------------------------------------------------
/extension/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Rahul Kadyan
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/extension/icon.svg:
--------------------------------------------------------------------------------
1 |
25 |
--------------------------------------------------------------------------------
/extension/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vuedx/preview/d47255746d42a5a423037718ef8d8b62b5a692f0/extension/logo.png
--------------------------------------------------------------------------------
/extension/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "version": "0.4.1",
4 | "name": "preview",
5 | "publisher": "znck",
6 | "displayName": "Preview",
7 | "description": "A storyboarding and prototyping tool for Vue.",
8 | "main": "dist/extension.js",
9 | "icon": "logo.png",
10 | "buildConfig": {
11 | "useMain": false,
12 | "sources": {
13 | "src/index.ts": [
14 | {
15 | "format": "commonjs",
16 | "file": "dist/extension.js",
17 | "bundle": {
18 | "external": [
19 | "vscode"
20 | ]
21 | }
22 | }
23 | ]
24 | },
25 | "external": [
26 | "vscode"
27 | ]
28 | },
29 | "repository": {
30 | "type": "git",
31 | "url": "https://github.com/znck/preview.git",
32 | "directory": "extension"
33 | },
34 | "files": [
35 | "dist",
36 | "logo.png"
37 | ],
38 | "activationEvents": [
39 | "onCommand:preview.show",
40 | "onCommand:preview.open",
41 | "onCommand:preview.stop",
42 | "onLanguage:vue"
43 | ],
44 | "contributes": {
45 | "commands": [
46 | {
47 | "command": "preview.show",
48 | "enablement": "resourceLangId == vue",
49 | "title": "Show preview",
50 | "category": "Preview",
51 | "icon": "$(open-preview)"
52 | },
53 | {
54 | "command": "preview.showSource",
55 | "enablement": "preview:isFocused",
56 | "title": "Show .vue file",
57 | "category": "Preview",
58 | "icon": "$(go-to-file)"
59 | },
60 | {
61 | "command": "preview.open",
62 | "enablement": "resourceLangId == vue || preview:isFocused",
63 | "title": "Open preview in browser",
64 | "category": "Preview",
65 | "icon": "$(browser)"
66 | },
67 | {
68 | "command": "preview.update",
69 | "enablement": "preview:isViteStarted",
70 | "title": "Update @vuedx/preview package",
71 | "category": "Preview",
72 | "icon": "$(arrow-up)"
73 | },
74 | {
75 | "command": "preview.stop",
76 | "enablement": "preview:isViteStarted",
77 | "title": "Stop preview server",
78 | "category": "Preview",
79 | "icon": "$(stop)"
80 | },
81 | {
82 | "command": "preview.refresh",
83 | "enablement": "preview:isFocused",
84 | "title": "Referh preview",
85 | "category": "Preview",
86 | "icon": "$(refresh)"
87 | }
88 | ],
89 | "keybindings": [
90 | {
91 | "command": "preview.refresh",
92 | "key": "ctrl+r",
93 | "mac": "cmd+r",
94 | "when": "preview:isFocused"
95 | }
96 | ],
97 | "menus": {
98 | "editor/title": [
99 | {
100 | "when": "resourceLangId == vue",
101 | "command": "preview.show",
102 | "group": "navigation"
103 | },
104 | {
105 | "when": "preview:isFocused",
106 | "command": "preview.showSource",
107 | "group": "navigation"
108 | },
109 | {
110 | "when": "preview:isFocused",
111 | "command": "preview.refresh",
112 | "group": "navigation"
113 | },
114 | {
115 | "when": "preview:isFocused || resourceLangId == vue",
116 | "command": "preview.open",
117 | "group": "z_commands"
118 | },
119 | {
120 | "when": "preview:isFocused && preview:isViteStarted",
121 | "command": "preview.stop",
122 | "group": "z_commands"
123 | }
124 | ]
125 | },
126 | "languages": [
127 | {
128 | "id": "vue",
129 | "extensions": [
130 | ".vue.p"
131 | ]
132 | }
133 | ]
134 | },
135 | "extensionDependencies": [
136 | "znck.vue"
137 | ],
138 | "engines": {
139 | "vscode": "^1.63.0"
140 | },
141 | "devDependencies": {
142 | "@types/node": "^14.14.22",
143 | "@types/node-fetch": "^2.5.8",
144 | "@types/vscode": "^1.63.0",
145 | "@vuedx/preview": "workspace:*",
146 | "get-port": "^5.1.1",
147 | "node-fetch": "^3.2.0",
148 | "semver": "^7.3.5",
149 | "vsce": "^2.6.7"
150 | },
151 | "scripts": {
152 | "build": "node scripts/build.mjs",
153 | "release": "vsce publish -p ${VSCODE_MARKETPLACE_TOKEN} --packagePath ./preview.vsix",
154 | "pre-release": "vsce publish --pre-release -p ${VSCODE_MARKETPLACE_TOKEN} --packagePath ./preview.vsix"
155 | }
156 | }
157 |
--------------------------------------------------------------------------------
/extension/readme.md:
--------------------------------------------------------------------------------
1 | # Preview
2 |
3 | This extension provides quick preview for `.vue` files.
4 |
5 | ## Support
6 |
7 | This extension is part of [VueDX project](https://github.com/znck/vue-developer-experience), maintained by [Rahul Kadyan](https://github.com/znck). You can [💖 sponsor him](https://github.com/sponsors/znck) for continued development of this extension and other VueDX tools.
8 |
9 |
10 |
11 |
12 | ---
13 |
14 | > Made with 💚 for Vue Developers.
15 | > — [Rahul Kadyan](https://znck.me) ([znck0](https://twitter.com/znck0))
16 |
--------------------------------------------------------------------------------
/extension/scripts/build.mjs:
--------------------------------------------------------------------------------
1 | import { execSync } from 'child_process';
2 | import fetch, { Headers } from 'node-fetch';
3 | import FS from 'node:fs';
4 | import Path from 'node:path';
5 | import semver from 'semver';
6 | import { fileURLToPath } from 'url';
7 |
8 | /**
9 | * @param {string} itemName
10 | */
11 | async function findLatestVersion(itemName) {
12 | const response = await fetch(
13 | 'https://marketplace.visualstudio.com/_apis/public/gallery/extensionquery',
14 | {
15 | method: 'POST',
16 | headers: new Headers({
17 | Accept: 'application/json;api-version=7.1-preview.1;excludeUrls=true',
18 | 'Content-Type': 'application/json',
19 | }),
20 | body: JSON.stringify({
21 | assetTypes: null,
22 | filters: [
23 | {
24 | criteria: [{ filterType: 7, value: itemName }],
25 | direction: 2,
26 | pageSize: 100,
27 | pageNumber: 1,
28 | sortBy: 0,
29 | sortOrder: 0,
30 | pagingToken: null,
31 | },
32 | ],
33 | flags: 2151,
34 | }),
35 | }
36 | );
37 |
38 | const body =
39 | /** @type {{results: Array<{extensions: Array<{versions: Array<{version: string}>}>}>}} */ (
40 | await response.json()
41 | );
42 |
43 | const extension = body.results
44 | .flatMap((result) => result.extensions)
45 | .find((extension) => extension != null);
46 |
47 | if (extension != null) return extension.versions[0]?.version;
48 |
49 | return undefined;
50 | }
51 |
52 | const fileNames = ['package.json'];
53 | export function getDir() {
54 | const arg = process.argv[2] ?? process.cwd();
55 | const dir = Path.isAbsolute(arg) ? arg : Path.resolve(process.cwd(), arg);
56 | return dir;
57 | }
58 |
59 | /**
60 | * @param {string} dir
61 | */
62 | function backup(dir) {
63 | for (const fileName of fileNames) {
64 | const file = Path.resolve(dir, fileName);
65 | const fileBak = Path.resolve(dir, `${fileName}.bak`);
66 |
67 | FS.writeFileSync(fileBak, FS.readFileSync(file, 'utf-8'));
68 | }
69 | }
70 |
71 | /**
72 | * @param {string} dir
73 | */
74 | function revert(dir) {
75 | for (const fileName of fileNames) {
76 | const file = Path.resolve(dir, fileName);
77 | const fileBak = Path.resolve(dir, `${fileName}.bak`);
78 |
79 | if (FS.existsSync(fileBak)) {
80 | FS.writeFileSync(file, FS.readFileSync(fileBak, 'utf-8'));
81 | FS.unlinkSync(fileBak);
82 | }
83 | }
84 | }
85 |
86 | /**
87 | * @param {string} current
88 | * @param {string} [latest]
89 | */
90 | function getNextPreReleaseVersion(current, latest) {
91 | let target = current;
92 | const v1 = semver.parse(current);
93 | const v2 = semver.parse(latest);
94 |
95 | if (v1 == null) return semver.inc(target, 'minor');
96 | if (v2 == null || latest == null) {
97 | return semver.inc(target, v1.minor % 2 === 0 ? 'minor' : 'patch');
98 | }
99 |
100 | if (v2.compare(v1) > 0) target = latest;
101 |
102 | return semver.inc(target, semver.minor(target) % 2 === 0 ? 'minor' : 'patch');
103 | }
104 |
105 | /**
106 | *
107 | * @param {string} dir
108 | */
109 | async function transform(dir) {
110 | const pkg = JSON.parse(FS.readFileSync(`${dir}/package.json`, 'utf-8'));
111 | const RELEASE_CHANNEL = /** @type {'release'|'pre-release'} */ (
112 | process.env['RELEASE_CHANNEL'] ?? 'release'
113 | );
114 |
115 | delete pkg.dependencies;
116 | delete pkg.devDependencies;
117 |
118 | if (RELEASE_CHANNEL === 'pre-release') {
119 | pkg['pre-release'] = true;
120 | pkg.version = getNextPreReleaseVersion(
121 | pkg.version,
122 | await findLatestVersion(`${pkg.publisher}.${pkg.name}`)
123 | );
124 | console.debug('Setting version to', pkg.version);
125 | } else {
126 | if (Array.isArray(pkg.contributions?.jsonValidation)) {
127 | pkg.contributions.jsonValidation[0].url = `https://unpkg.com/@vuedx/projectconfig@${pkg.version}/schema.json`;
128 | }
129 | }
130 |
131 | const packageFile = Path.resolve(dir, 'package.json');
132 |
133 | FS.writeFileSync(packageFile, JSON.stringify(pkg, null, 2));
134 | }
135 |
136 | /**
137 | * @param {string} dir
138 | * @param {() => void} fn
139 | */
140 | async function prepareExtensionForPackaging(dir, fn) {
141 | try {
142 | backup(dir);
143 | await transform(dir);
144 | fn();
145 | } finally {
146 | revert(dir);
147 | }
148 | }
149 |
150 | const __dirname = Path.dirname(fileURLToPath(import.meta.url));
151 | const dir = Path.resolve(__dirname, '..');
152 | prepareExtensionForPackaging(dir, () => {
153 | const execArgs = { stdio: [0, 1, 2], cwd: dir };
154 | const RELEASE_CHANNEL = /** @type {'release'|'pre-release'} */ (
155 | process.env['RELEASE_CHANNEL'] ?? 'release'
156 | );
157 | const args = RELEASE_CHANNEL === 'pre-release' ? '--pre-release' : '';
158 |
159 | execSync(`$(pnpm bin)/vsce package --no-dependencies ${args} --out preview.vsix`, execArgs);
160 | });
161 |
--------------------------------------------------------------------------------
/extension/src/getWebviewContent.ts:
--------------------------------------------------------------------------------
1 | // eslint-disable-next-line @typescript-eslint/triple-slash-reference
2 | ///
3 |
4 | export interface EventData {
5 | kind: 'event';
6 | payload: {
7 | type: 'keyup' | 'keydown';
8 | init: KeyboardEventInit;
9 | };
10 | }
11 |
12 | export function getWebviewContent(body: string): string {
13 | return `
14 |
15 |
16 |
17 |
18 | Preview
19 |
27 |
28 |
29 | ${body}
30 |
65 |
66 | `;
67 | }
68 |
--------------------------------------------------------------------------------
/extension/src/global.d.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/naming-convention */
2 | /* eslint-disable no-var */
3 | declare var __DEV__: boolean;
4 | declare var __PROD__: boolean;
5 | declare var __PREVIEW_INSTALLATION_SOURCE__: string;
6 |
--------------------------------------------------------------------------------
/extension/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.base.json",
3 | "include": ["src"]
4 | }
5 |
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('@jest/types').Config.InitialOptions} */
2 | const config = {
3 | verbose: true,
4 | testMatch: [],
5 | rootDir: __dirname,
6 | moduleFileExtensions: ['ts', 'mjs', 'js', 'cjs', 'json', 'vue'],
7 | projects: ['/packages/*/jest.config.js'],
8 | };
9 |
10 | module.exports = config;
11 |
--------------------------------------------------------------------------------
/lint-staged.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | '**/*.{ts,mjs,cjs,js,vue}': ['eslint --quiet --fix --cache', 'prettier --write'],
3 | '*.{css,md}': ['prettier --write'],
4 | '**/*.{json,graphql}': ['prettier --write'],
5 | '.github/**/*.{yaml,yml}': [
6 | 'node scripts/prepare-workflows.mjs',
7 | 'git add .github/workflows',
8 | 'prettier --write',
9 | ],
10 | };
11 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "name": "workspace",
4 | "engines": {
5 | "node": ">=14.0.0"
6 | },
7 | "devDependencies": {
8 | "@changesets/cli": "^2.20.0",
9 | "@rollup/plugin-commonjs": "^19.0.2",
10 | "@rollup/plugin-node-resolve": "^13.1.3",
11 | "@rollup/plugin-replace": "^2.4.2",
12 | "@rollup/plugin-typescript": "^8.3.0",
13 | "@typescript-eslint/eslint-plugin": "^4.33.0",
14 | "@typescript-eslint/parser": "^4.33.0",
15 | "@vuedx/eslint-config": "^0.0.2",
16 | "@vuedx/monorepo-tools": "^0.2.1",
17 | "eslint": "^7.32.0",
18 | "husky": "^6.0.0",
19 | "jest": "^26.6.3",
20 | "js-yaml": "^4.1.0",
21 | "lint-staged": "^11.2.6",
22 | "prettier": "^2.5.1",
23 | "rollup": "^2.66.1",
24 | "ts-jest": "^26.5.6",
25 | "tslib": "^2.3.1",
26 | "typescript": "4.3.2"
27 | },
28 | "scripts": {
29 | "build": "rollup -c --environment BUILD:production && pnpm recursive --filter ./packages/preview-shell run build",
30 | "dev:build": "rollup -c --environment BUILD:development",
31 | "watch": "rollup -c -w",
32 | "test": "jest",
33 | "coverage": "jest --coverage",
34 | "start": "node packages/preview/bin/preview.js packages/example",
35 | "changeset": "changeset",
36 | "prepare": "husky install"
37 | },
38 | "pnpm": {
39 | "overrides": {
40 | "vite": "2.5.0",
41 | "typescript": "4.3.2"
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/packages/preview-compiler/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # @vuedx/preview-compiler
2 |
3 | ## 0.2.2
4 |
5 | ### Patch Changes
6 |
7 | - dd448e6: Support async imports in `` blocks
8 | - dd448e6: Sync color-scheme with VS Code
9 |
10 | ## 0.2.1
11 |
12 | ### Patch Changes
13 |
14 | - b0eea6a: Build using @vuedx/monorepo-tools
15 |
16 | ## 0.2.0
17 |
18 | ### Minor Changes
19 |
20 | - b03452d: Add HMR support
21 |
22 | ## 0.1.3
23 |
24 | ### Patch Changes
25 |
26 | - 1b39181: Bump all versions to test continuous release CI
27 |
28 | ## 0.1.2
29 |
30 | ### Patch Changes
31 |
32 | - 18a1b02: Bump version
33 |
34 | ## 0.1.1
35 |
36 | ### Patch Changes
37 |
38 | - e3ba132: Update HMR API
39 |
40 | - Version lock on vite@2.0.0-beta.44
41 | - Remove forked HMR client and use `/@vite/client` instead
42 |
43 | ## 0.1.0
44 |
45 | ### Minor Changes
46 |
47 | - d34a262: VS Code preview extension
48 |
--------------------------------------------------------------------------------
/packages/preview-compiler/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@vuedx/preview-compiler",
3 | "version": "0.2.2",
4 | "main": "dist/preview-compiler.js",
5 | "module": "dist/preview-compiler.esm.js",
6 | "types": "src/index.ts",
7 | "publishConfig": {
8 | "types": "dist/preview-compiler.d.ts"
9 | },
10 | "files": [
11 | "dist"
12 | ],
13 | "dependencies": {
14 | "@vue/compiler-dom": "^3.0.5",
15 | "@vuedx/template-ast-types": "^0.6.0"
16 | },
17 | "devDependencies": {
18 | "@vue/compiler-core": "^3.0.5",
19 | "typescript": "^4.1.3"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/packages/preview-compiler/src/index.ts:
--------------------------------------------------------------------------------
1 | import type { NodeTransform } from '@vue/compiler-core';
2 | import { compile as baseCompile } from '@vue/compiler-dom';
3 | import {
4 | isAttributeNode,
5 | isDirectiveNode,
6 | isElementNode,
7 | isRootNode,
8 | isSimpleExpressionNode,
9 | } from '@vuedx/template-ast-types';
10 | import * as Path from 'path';
11 |
12 | interface SetupOutput {
13 | components: string;
14 | requests: string;
15 | state: string;
16 | async?: boolean;
17 | }
18 |
19 | function createPreviewSetupTransform(output: SetupOutput): NodeTransform {
20 | return (node, context) => {
21 | if (isRootNode(node)) {
22 | context.addIdentifiers('$p');
23 | } else if (isRootNode(context.parent) && isElementNode(node) && node.tag === 'setup') {
24 | context.removeNode();
25 |
26 | node.props.forEach((prop) => {
27 | if (isAttributeNode(prop)) {
28 | output.async = prop.value != null ? prop.value.content.toLowerCase() === 'true' : true;
29 | } else if (isDirectiveNode(prop) && prop.name === 'bind') {
30 | if (isSimpleExpressionNode(prop.arg) && prop.arg.isStatic) {
31 | if (
32 | prop.arg.content === 'components' ||
33 | prop.arg.content === 'requests' ||
34 | prop.arg.content === 'state'
35 | ) {
36 | if (prop.exp != null) {
37 | output[prop.arg.content] = prop.exp.loc.source;
38 | }
39 | }
40 | }
41 | }
42 | });
43 | }
44 | };
45 | }
46 |
47 | export interface CompileOptions {
48 | componentFileName: string;
49 | allowOverrides?: boolean | string;
50 | attrs?: Record;
51 | imports?: Record;
52 | hmrId?: string;
53 | }
54 |
55 | export function compile(
56 | content: string,
57 | { componentFileName, hmrId, allowOverrides, attrs = {}, imports = {} }: CompileOptions
58 | ): string {
59 | const setup: SetupOutput = {
60 | components: '{}',
61 | requests: '{}',
62 | state: '{}',
63 | };
64 | const result = baseCompile(content, {
65 | inline: false,
66 | mode: 'module',
67 | sourceMap: false,
68 | hoistStatic: true,
69 | cacheHandlers: true,
70 | expressionPlugins: ['typescript', 'dynamicImport'],
71 | nodeTransforms: [createPreviewSetupTransform(setup)],
72 | });
73 | const componentName = (componentFileName.split(Path.sep).pop() ?? 'self').replace(/\.vue$/, '');
74 |
75 | const preamble = getCode(
76 | result.preamble,
77 | `import * as _Vue from ${JSON.stringify(imports['vue'] ?? 'vue')}`,
78 | `import * as _Preview from ${JSON.stringify(
79 | imports['@vuedx/preview-provider'] ?? '@vuedx/preview-provider'
80 | )}`,
81 | `import _component_self from '${componentFileName.replace(/\\/g, '/')}'`,
82 | `_Preview.installFetchInterceptor()`,
83 | result.code
84 | );
85 |
86 | const source = `_Vue.defineComponent({
87 | name: 'Preview(${componentName}):${String(attrs['name'] ?? 'unnamed')}',
88 | inheritAttrs: false,
89 | _file: ${JSON.stringify(componentFileName)},
90 | components: { "${componentName}": _component_self },
91 | ${setup.async === true ? 'async ' : ''}setup() {
92 | _Preview.setActiveComponent({
93 | ...(${JSON.stringify(attrs)}),
94 | componentName: "${componentName}",
95 | })
96 | const $p = {
97 | ..._Preview.provider,
98 | attrs: ${JSON.stringify(attrs)},
99 | state: _Vue.reactive(_overrides.state != null ? _overrides.state : ${setup.state}),
100 | x: _Vue.inject('preview:UserProviders', null),
101 | }
102 |
103 | _Preview.useRequests({ ...(${setup.requests}), ..._overrides.requests })
104 | _Preview.useComponents({...(${setup.components}), ..._overrides.components})
105 |
106 | return { preview: $p }
107 | },
108 | created() {
109 | this.$p = this.preview
110 | },
111 | render,
112 | })`.trim();
113 |
114 | if (hmrId != null) {
115 | return getCode(
116 | preamble,
117 | `const _overrides = {}`,
118 | `const _preview_main = ${source}`,
119 | generateHMR(hmrId),
120 | `export default _preview_main`
121 | );
122 | } else if (allowOverrides != null) {
123 | const fn = typeof allowOverrides === 'string' ? allowOverrides : '';
124 | return getCode(preamble, `export default (_overrides = {}) => ${fn}(${source})`);
125 | } else {
126 | return getCode(preamble, `export default (${source})`);
127 | }
128 | }
129 |
130 | function generateHMR(hmrId: string): string {
131 | return `
132 | _preview_main.__hmrId = '${hmrId}'
133 | typeof __VUE_HMR_RUNTIME__ !== 'undefined' && __VUE_HMR_RUNTIME__.createRecord(_preview_main.__hmrId, _preview_main)
134 | if (import.meta.hot) {
135 | import.meta.hot.accept(({ default: updated }) => {
136 | __VUE_HMR_RUNTIME__.reload(updated.__hmrId, updated)
137 | })
138 | }`.trim();
139 | }
140 |
141 | function getCode(...lines: Array): string {
142 | return lines.filter(Boolean).join('\n');
143 | }
144 |
--------------------------------------------------------------------------------
/packages/preview-compiler/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "include": ["src"]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/preview-provider/.npmignore:
--------------------------------------------------------------------------------
1 | dist/types/
2 |
--------------------------------------------------------------------------------
/packages/preview-provider/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # @vuedx/preview-provider
2 |
3 | ## 0.1.5
4 |
5 | ### Patch Changes
6 |
7 | - dd448e6: Support async imports in `` blocks
8 | - dd448e6: Sync color-scheme with VS Code
9 |
10 | ## 0.1.4
11 |
12 | ### Patch Changes
13 |
14 | - b0eea6a: Build using @vuedx/monorepo-tools
15 |
16 | ## 0.1.3
17 |
18 | ### Patch Changes
19 |
20 | - 1b39181: Bump all versions to test continuous release CI
21 |
22 | ## 0.1.2
23 |
24 | ### Patch Changes
25 |
26 | - 18a1b02: Bump version
27 |
28 | ## 0.1.1
29 |
30 | ### Patch Changes
31 |
32 | - e3ba132: Update HMR API
33 |
34 | - Version lock on vite@2.0.0-beta.44
35 | - Remove forked HMR client and use `/@vite/client` instead
36 |
37 | - e3ba132: Use `vue` instead of `@vue/runtime-core`
38 |
39 | ## 0.1.0
40 |
41 | ### Minor Changes
42 |
43 | - d34a262: VS Code preview extension
44 |
--------------------------------------------------------------------------------
/packages/preview-provider/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@vuedx/preview-provider",
3 | "version": "0.1.5",
4 | "peerDependencies": {
5 | "vue": "^3.0.5"
6 | },
7 | "peerDependenciesMeta": {
8 | "vue": {
9 | "optional": true
10 | }
11 | },
12 | "main": "dist/preview-provider.js",
13 | "module": "dist/preview-provider.mjs",
14 | "types": "src/index.ts",
15 | "publishConfig": {
16 | "types": "dist/preview-provider.d.ts"
17 | },
18 | "buildConfig": {
19 | "external": [
20 | "vue"
21 | ]
22 | },
23 | "files": [
24 | "dist"
25 | ],
26 | "devDependencies": {
27 | "typescript": "^4.1.3",
28 | "vue": "^3.0.5"
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/packages/preview-provider/src/activeComponent.ts:
--------------------------------------------------------------------------------
1 | export interface ActiveComponent {
2 | name?: string;
3 | componentName: string;
4 | }
5 |
6 | let activeComponent: ActiveComponent = {
7 | componentName: '',
8 | };
9 |
10 | export function setActiveComponent(component: ActiveComponent): void {
11 | activeComponent = component;
12 | }
13 |
14 | export function getActiveComponent(): ActiveComponent {
15 | return activeComponent;
16 | }
17 |
--------------------------------------------------------------------------------
/packages/preview-provider/src/communication.ts:
--------------------------------------------------------------------------------
1 | export function notify(
2 | event: 'missing-request-handler',
3 | payload: any
4 | ): void;
5 |
6 | export function notify(event: string, payload: any): void {
7 | console.log('[preview]', event, payload);
8 | window.dispatchEvent(new CustomEvent('preview:notify', { detail: { event, payload } }));
9 | }
10 |
--------------------------------------------------------------------------------
/packages/preview-provider/src/components.ts:
--------------------------------------------------------------------------------
1 | // eslint-disable-next-line @typescript-eslint/prefer-ts-expect-error
2 | // @ts-ignore - export * is not supported from rollup-plugin-dts
3 | import { Component, defineAsyncComponent, getCurrentInstance } from 'vue';
4 |
5 | export function useComponents(components: Record): void {
6 | const instance = getCurrentInstance();
7 | if (instance != null) {
8 | const { app } = instance.appContext;
9 |
10 | Object.entries(components).forEach(([name, component]) => {
11 | app.component(
12 | name,
13 | typeof component === 'function' ? defineAsyncComponent(component as any) : component
14 | );
15 | });
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/packages/preview-provider/src/fetch.ts:
--------------------------------------------------------------------------------
1 | // eslint-disable-next-line @typescript-eslint/prefer-ts-expect-error
2 | // @ts-ignore - rollup-plugin-dts does not work with export *
3 | import { getCurrentInstance, onUnmounted } from 'vue';
4 | import { getActiveComponent } from './activeComponent';
5 | import { notify } from './communication';
6 | type RequestHandler = (request: Request) => any;
7 |
8 | export type RequestOptions = Record>;
9 |
10 | interface InterceptorRecord {
11 | name: string;
12 | matcher(request: Request): Promise | boolean;
13 | handler: RequestHandler;
14 | }
15 |
16 | const state: { interceptors: InterceptorRecord[]; warned: Set } = {
17 | interceptors: [],
18 | warned: new Set('/'),
19 | };
20 | const REQUEST_RE = /^(GET|POST|PUT|DELETE|HEAD)(\|(GET|POST|PUT|DELETE|HEAD))*\s+/i;
21 | const GRAPHQL_RE = /^(QUERY|MUTATION)\s+/i;
22 |
23 | /**
24 | * @param options register request handlers/interceptors.
25 | */
26 | export function useRequests(options: RequestOptions): void {
27 | state.interceptors = Object.entries(options).map(([key, value]): InterceptorRecord => {
28 | const handler = (typeof value === 'function' ? value : () => value) as any;
29 | const graphql = GRAPHQL_RE.exec(key);
30 | if (graphql != null) {
31 | const [type, url, pattern] = key.split(/[ ]+/) as [string, string] | [string, string, string];
32 | return {
33 | name: key,
34 | matcher: async (request) => {
35 | if (!matches(request.url, url)) return false;
36 |
37 | let query: string | null = null;
38 | if (type?.toUpperCase() === 'QUERY') {
39 | if (request.method === 'GET') {
40 | query = getSearchParams(request.url).get('query');
41 | }
42 | }
43 |
44 | if (request.method === 'POST') {
45 | try {
46 | const body = await request.clone().json();
47 | query = typeof body.query === 'string' ? body.query : null;
48 | } catch {}
49 | }
50 |
51 | if (query != null) {
52 | if (pattern == null) return true;
53 | else
54 | return matchPattern(query.replace(/[ \r\n]/g, ''), pattern.replace(/[ \r\n]/g, ''));
55 | }
56 |
57 | return false;
58 | },
59 | handler,
60 | };
61 | }
62 | const http = REQUEST_RE.exec(key);
63 | if (http != null) {
64 | const [type, url] = key.split(/[ ]+/) as [string, string];
65 | const methods = new Set(type.toUpperCase().split('|'));
66 |
67 | console.log(http);
68 |
69 | return {
70 | name: key,
71 | matcher: (request) => methods.has(request.method) && matches(request.url, url),
72 | handler,
73 | };
74 | }
75 |
76 | return {
77 | name: key,
78 | matcher: (request) => {
79 | return matches(request.url, key);
80 | },
81 | handler,
82 | };
83 | });
84 |
85 | if (getCurrentInstance() != null) {
86 | onUnmounted(() => {
87 | state.interceptors = [];
88 | });
89 | }
90 | }
91 |
92 | function matches(url: string, pattern: string): boolean {
93 | if (pattern === '*') return true;
94 | if (pattern.includes('*')) {
95 | return matchPattern(pattern.startsWith('/') ? getPathName(url) : url, pattern);
96 | }
97 | if (pattern.startsWith('/')) {
98 | return getPathName(url) === pattern;
99 | }
100 |
101 | return url === pattern;
102 | }
103 |
104 | function matchPattern(text: string, pattern: string): boolean {
105 | if (pattern.includes('*')) {
106 | const RE = new RegExp(`^${pattern.replace(/\*/g, '.*?')}$`);
107 |
108 | return RE.test(text);
109 | } else {
110 | return text === pattern;
111 | }
112 | }
113 |
114 | /**
115 | * Install fetch interceptors.
116 | */
117 | export function installFetchInterceptor(): void {
118 | const fetch = window.fetch;
119 |
120 | window.fetch = async function (input: RequestInfo, init?: RequestInit): Promise {
121 | const request =
122 | input instanceof Request ? new Request(input.clone(), init) : new Request(input, init);
123 | const interceptor = await getInterceptor(request);
124 |
125 | if (interceptor != null) {
126 | const active = getActiveComponent();
127 | const group = active.name != null ? `[${active.name}] ` : '';
128 | console.groupCollapsed(`${group}Request handled by: ${interceptor.name}`);
129 | console.debug(request);
130 | console.groupEnd();
131 | const result = await interceptor.handler(request);
132 | const encode = (result: unknown): Response => {
133 | if (result instanceof Response) return result;
134 | else if (typeof result === 'string')
135 | return new Response(result, {
136 | status: 200,
137 | statusText: 'OK',
138 | headers: { 'Content-Type': 'text/plain' },
139 | });
140 | else
141 | return new Response(JSON.stringify(result), {
142 | status: 200,
143 | statusText: 'OK',
144 | headers: { 'Content-Type': 'application/json' },
145 | });
146 | };
147 |
148 | const response = encode(result.default != null ? result.default : result);
149 |
150 | const copy = response.clone();
151 |
152 | const body =
153 | copy.headers.get('Content-Type') === 'application/json'
154 | ? await copy.json()
155 | : await copy.text();
156 |
157 | console.groupCollapsed(`${group}Response from: ${interceptor.name}`);
158 | console.debug(copy);
159 | console.debug(body);
160 | console.groupEnd();
161 | return response;
162 | }
163 |
164 | const id = `${request.method} ${request.url}`;
165 | if (!state.warned.has(id)) {
166 | state.warned.add(id);
167 | notify('missing-request-handler', { request, body: await request.clone().text() });
168 | }
169 |
170 | return fetch(request);
171 | };
172 | }
173 |
174 | async function getInterceptor(request: Request): Promise {
175 | for (const interceptor of state.interceptors) {
176 | if (await interceptor.matcher(request)) {
177 | return interceptor;
178 | }
179 | }
180 |
181 | return null;
182 | }
183 |
184 | function getPathName(url: string): string {
185 | try {
186 | return new URL(url).pathname;
187 | } catch {
188 | return url.replace(/^[^/]+:\/\/[^/]+/, '');
189 | }
190 | }
191 |
192 | function getSearchParams(url: string): URLSearchParams {
193 | try {
194 | return new URL(url).searchParams;
195 | } catch {
196 | const index = url.indexOf('?');
197 |
198 | return index < 0 ? new URLSearchParams() : new URLSearchParams(url.substr(index + 1));
199 | }
200 | }
201 |
--------------------------------------------------------------------------------
/packages/preview-provider/src/index.ts:
--------------------------------------------------------------------------------
1 | // eslint-disable-next-line @typescript-eslint/prefer-ts-expect-error
2 | // @ts-ignore - export * is not supported from rollup-plugin-dts
3 | import type { Component } from 'vue';
4 | import { getActiveComponent, setActiveComponent } from './activeComponent';
5 | import { useComponents } from './components';
6 | import { installFetchInterceptor, RequestOptions, useRequests } from './fetch';
7 | import { provider } from './utilities';
8 |
9 | export interface SetupOptions {
10 | requests: RequestOptions;
11 | components: Record;
12 | state: T;
13 | }
14 |
15 | export {
16 | provider,
17 | useRequests,
18 | useComponents,
19 | installFetchInterceptor,
20 | setActiveComponent,
21 | getActiveComponent,
22 | };
23 |
--------------------------------------------------------------------------------
/packages/preview-provider/src/utilities.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/prefer-ts-expect-error */
2 | // @ts-ignore - export * is not supported from rollup-plugin-dts
3 | import { Component, defineComponent, h } from 'vue';
4 |
5 | export function repeat(count: number, generator: (index: number) => T): T[] {
6 | const items: T[] = [];
7 | for (let i = 0; i < count; ++i) {
8 | items.push(generator(i));
9 | }
10 | return items;
11 | }
12 |
13 | export function join(items: any[], glue: string = ' '): string {
14 | return items.filter((item) => item == null).join(glue);
15 | }
16 |
17 | export function createFunctionObject(fn: F, ob: O): F & O {
18 | return Object.assign(fn, ob);
19 | }
20 |
21 | /**
22 | * A collection of string generators.
23 | */
24 | export const string = createFunctionObject(() => '', {
25 | /**
26 | * Get a random person's name.
27 | */
28 | person: createFunctionObject(
29 | () => join([string.person.firstName(), string.person.middleName(), string.person.lastName()]),
30 | {
31 | firstName: () => '',
32 | middleName: () => '',
33 | lastName: () => '',
34 | }
35 | ),
36 | });
37 |
38 | /**
39 | * A collection of number generators.
40 | */
41 | export const number = createFunctionObject(() => number.any(), {
42 | any: () => (Math.random() <= 0.5 ? number.negative() : number.positive()),
43 | positive: () => Math.random() * Number.MAX_VALUE,
44 | negative: () => Math.random() * Number.MIN_VALUE,
45 | in: (start: number, end: number) => Math.min(start, end) + Math.abs(end - start) * Math.random(),
46 | int: createFunctionObject(() => number.int.any(), {
47 | any: () => (Math.random() <= 0.5 ? number.int.negative() : number.int.positive()),
48 | positive: () => Math.floor(Math.random() * Number.MAX_SAFE_INTEGER),
49 | negative: () => Math.ceil(Math.random() * Number.MIN_SAFE_INTEGER),
50 | in: (start: number, end: number) =>
51 | Math.floor(Math.min(start, end) + Math.abs(end - start) * Math.random()),
52 | }),
53 |
54 | percentage: () => number.in(0, 100),
55 | });
56 |
57 | export const bool = createFunctionObject(() => Math.random() <= 0.5, {});
58 | export const on = createFunctionObject(
59 | (name: string) => (event: any) => console.log(name, event),
60 | {}
61 | );
62 |
63 | export function defineStubComponent(name: string, component: Component): Component {
64 | return defineComponent({
65 | inheritAttrs: false,
66 | name: `stub-${name}`,
67 | // eslint-disable-next-line @typescript-eslint/prefer-ts-expect-error
68 | // @ts-ignore — rollup-pugin-dts
69 | setup: (props, { slots, attrs }) => {
70 | return () =>
71 | h(
72 | 'div',
73 | {
74 | style: `
75 | background: #F7E9D4;
76 | color: #B27603;
77 | display: inline-block;
78 | padding: 4px 8px;
79 | border: 1px dashed #B27603;
80 | box-sizing: border-box;
81 | `,
82 | },
83 | [h(component, { ...attrs, ...props } as any, slots)]
84 | );
85 | },
86 | });
87 | }
88 |
89 | export const stub = createFunctionObject(
90 | (content: string = 'stubbed component'): Component => stub.static(content),
91 | {
92 | static: (content: string): Component =>
93 | defineStubComponent('anonymous', defineComponent({ render: () => content })),
94 | showProps: (): Component =>
95 | defineStubComponent(
96 | 'anonymous',
97 | // prettier-ignore
98 | // @ts-ignore - rollup-plugin-dts does not work with export *
99 | defineComponent((_, { attrs }) => () =>
100 | h('pre', null, JSON.stringify(attrs, null, 2))
101 | )
102 | ),
103 | }
104 | );
105 |
106 | export const component = createFunctionObject(
107 | (): Component => defineComponent(() => () => h('div')),
108 | {
109 | image: createFunctionObject(
110 | (src?: string): Component => defineComponent(() => () => h('img', { src })),
111 | {
112 | unsplash: (query: string = 'random'): Component =>
113 | component.image(`https://unsplash.it/?q=${query}&id=${number.int.in(0, 50)}`),
114 | }
115 | ),
116 | }
117 | );
118 |
119 | const STATUS_TEXTS: Record = {
120 | 100: 'Continue',
121 | 101: 'Switching Protocols',
122 | 102: 'Processing',
123 | 103: 'Early Hints',
124 | 200: 'OK',
125 | 201: 'Created',
126 | 202: 'Accepted',
127 | 203: 'Non-Authoritative Information',
128 | 204: 'No Content',
129 | 205: 'Reset Content',
130 | 206: 'Partial Content',
131 | 207: 'Multi-Status',
132 | 208: 'Already Reported',
133 | 218: 'This is fine', // Apache
134 | 226: 'IM Used',
135 | 300: 'Multiple Choices',
136 | 301: 'Moved Permanently',
137 | 302: 'Found',
138 | 303: 'See Other',
139 | 304: 'Not Modified',
140 | 305: 'Use Proxy',
141 | 306: 'Switch Proxy',
142 | 307: 'Temporary Redirect',
143 | 308: 'Permanent Redirect',
144 | 400: 'Bad Request',
145 | 401: 'Unauthorized',
146 | 402: 'Payment Required',
147 | 403: 'Forbidden',
148 | 404: 'Not Found',
149 | 405: 'Method Not Allowed',
150 | 406: 'Not Acceptable',
151 | 407: 'Proxy Authentication Required',
152 | 408: 'Request Timeout',
153 | 409: 'Conflict',
154 | 410: 'Gone',
155 | 411: 'Length Required',
156 | 413: 'Payload Too Large',
157 | 414: 'URI Too Long',
158 | 415: 'Unsupported Media Type ',
159 | 416: 'Range Not Satisfiable',
160 | 417: 'Expectation Failed',
161 | 418: "I'm a teapot",
162 | 419: 'Page Expired', // Laravel
163 | 420: 'Method Failure', // Spring
164 | 421: 'Misdirected Request',
165 | 422: 'Unprocessable Entity',
166 | 423: 'Locked',
167 | 424: 'Failed Dependency',
168 | 425: 'Too Early',
169 | 426: 'Upgrade Required',
170 | 428: 'Precondition Required',
171 | 429: 'Too Many Requests',
172 | 431: 'Request Header Fields Too Large',
173 | 450: 'Blocked by Windows Parental Controls', // Microsoft
174 | 451: 'Unavailable For Legal Reasons',
175 | 500: 'Internal Server Error',
176 | 501: 'Not Implemented',
177 | 502: 'Bad Gateway',
178 | 503: 'Service Unavailable',
179 | 504: 'Gateway Timeout',
180 | 505: 'HTTP Version Not Supported',
181 | 506: 'Variant Also Negotiates',
182 | 507: 'Insufficient Storage',
183 | 508: 'Loop Detected',
184 | 510: 'Not Extended',
185 | 511: 'Network Authentication Required',
186 | };
187 |
188 | export const http = createFunctionObject(
189 | (body: BodyInit = '{}', status: number = 200) => http.send(body, status),
190 | {
191 | status: (status: number = 200) =>
192 | new Response(null, { status, statusText: STATUS_TEXTS[status] }),
193 | send: (body: BodyInit, status: number = 200) =>
194 | new Response(body, { status, statusText: STATUS_TEXTS[status] }),
195 | create: (body?: BodyInit, init?: ResponseInit) => new Response(body, init),
196 | delayed: async (delay: number, body?: BodyInit, init?: ResponseInit) => {
197 | return await new Promise((resolve) => {
198 | if (Number.isFinite(delay)) {
199 | setTimeout(() => {
200 | resolve(new Response(body, init));
201 | }, delay);
202 | }
203 | });
204 | },
205 | }
206 | );
207 |
208 | export const provider = { http, stub, component, number, string, bool, repeat, on };
209 |
--------------------------------------------------------------------------------
/packages/preview-provider/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "include": ["src"],
4 | "compilerOptions": {
5 | "lib": ["ESNext", "DOM"]
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/packages/preview-shell/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # @vuedx/preview-shell
2 |
3 | ## 0.3.0
4 |
5 | ### Minor Changes
6 |
7 | - ff930e3: Use `/__preview` path prefix for plugin mode
8 |
9 | BREAKING CHANGE: Internal virtual files also use `__preview` prefix, instead of `@preview`.
10 |
11 | ## 0.2.2
12 |
13 | ### Patch Changes
14 |
15 | - dd448e6: Support async imports in `` blocks
16 | - dd448e6: Sync color-scheme with VS Code
17 |
18 | ## 0.2.1
19 |
20 | ### Patch Changes
21 |
22 | - b0eea6a: Build using @vuedx/monorepo-tools
23 |
24 | ## 0.2.0
25 |
26 | ### Minor Changes
27 |
28 | - b03452d: Add HMR support
29 |
30 | ## 0.1.4
31 |
32 | ### Patch Changes
33 |
34 | - 83e89e5: Use consistent virtual resource scheme
35 |
36 | - Use `@preview:component/${type}.${ext}` for component scoped resources
37 | - Use `@preview:shell/` for proxying pre-built shell application
38 | - Use `@preview:components.js` for component index
39 | - Use `@preview:user/setup.js` for custom runtime setup file
40 |
41 | ## 0.1.3
42 |
43 | ### Patch Changes
44 |
45 | - 1b39181: Bump all versions to test continuous release CI
46 |
47 | ## 0.1.2
48 |
49 | ### Patch Changes
50 |
51 | - 18a1b02: Fix build in release pipeline
52 |
53 | ## 0.1.1
54 |
55 | ### Patch Changes
56 |
57 | - e3ba132: Update HMR API
58 |
59 | - Version lock on vite@2.0.0-beta.44
60 | - Remove forked HMR client and use `/@vite/client` instead
61 |
62 | ## 0.1.0
63 |
64 | ### Minor Changes
65 |
66 | - d34a262: VS Code preview extension
67 |
--------------------------------------------------------------------------------
/packages/preview-shell/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Preview
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/packages/preview-shell/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@vuedx/preview-shell",
3 | "version": "0.3.0",
4 | "scripts": {
5 | "start": "vite serve",
6 | "build": "vite build --base /__preview:shell/"
7 | },
8 | "files": [
9 | "dist"
10 | ],
11 | "buildConfig": {
12 | "useMain": false
13 | },
14 | "dependencies": {
15 | "vue": "^3.2.29",
16 | "vue-router": "^4.0.12"
17 | },
18 | "devDependencies": {
19 | "@vitejs/plugin-vue": "^1.4.0",
20 | "@vue/compiler-sfc": "^3.2.4",
21 | "autoprefixer": "^10.2.3",
22 | "browserlist": "^1.0.1",
23 | "magic-string": "^0.25.7",
24 | "postcss": "^8.3.6",
25 | "tailwindcss": "^2.0.2",
26 | "typescript": "^4.1.3",
27 | "vite": "^2.5.0"
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/packages/preview-shell/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | };
7 |
--------------------------------------------------------------------------------
/packages/preview-shell/public/browserconfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | #ffffff
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/packages/preview-shell/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vuedx/preview/d47255746d42a5a423037718ef8d8b62b5a692f0/packages/preview-shell/public/favicon.ico
--------------------------------------------------------------------------------
/packages/preview-shell/public/icons/android-icon-144x144.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vuedx/preview/d47255746d42a5a423037718ef8d8b62b5a692f0/packages/preview-shell/public/icons/android-icon-144x144.png
--------------------------------------------------------------------------------
/packages/preview-shell/public/icons/android-icon-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vuedx/preview/d47255746d42a5a423037718ef8d8b62b5a692f0/packages/preview-shell/public/icons/android-icon-192x192.png
--------------------------------------------------------------------------------
/packages/preview-shell/public/icons/android-icon-36x36.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vuedx/preview/d47255746d42a5a423037718ef8d8b62b5a692f0/packages/preview-shell/public/icons/android-icon-36x36.png
--------------------------------------------------------------------------------
/packages/preview-shell/public/icons/android-icon-48x48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vuedx/preview/d47255746d42a5a423037718ef8d8b62b5a692f0/packages/preview-shell/public/icons/android-icon-48x48.png
--------------------------------------------------------------------------------
/packages/preview-shell/public/icons/android-icon-72x72.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vuedx/preview/d47255746d42a5a423037718ef8d8b62b5a692f0/packages/preview-shell/public/icons/android-icon-72x72.png
--------------------------------------------------------------------------------
/packages/preview-shell/public/icons/android-icon-96x96.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vuedx/preview/d47255746d42a5a423037718ef8d8b62b5a692f0/packages/preview-shell/public/icons/android-icon-96x96.png
--------------------------------------------------------------------------------
/packages/preview-shell/public/icons/apple-icon-114x114.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vuedx/preview/d47255746d42a5a423037718ef8d8b62b5a692f0/packages/preview-shell/public/icons/apple-icon-114x114.png
--------------------------------------------------------------------------------
/packages/preview-shell/public/icons/apple-icon-120x120.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vuedx/preview/d47255746d42a5a423037718ef8d8b62b5a692f0/packages/preview-shell/public/icons/apple-icon-120x120.png
--------------------------------------------------------------------------------
/packages/preview-shell/public/icons/apple-icon-144x144.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vuedx/preview/d47255746d42a5a423037718ef8d8b62b5a692f0/packages/preview-shell/public/icons/apple-icon-144x144.png
--------------------------------------------------------------------------------
/packages/preview-shell/public/icons/apple-icon-152x152.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vuedx/preview/d47255746d42a5a423037718ef8d8b62b5a692f0/packages/preview-shell/public/icons/apple-icon-152x152.png
--------------------------------------------------------------------------------
/packages/preview-shell/public/icons/apple-icon-180x180.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vuedx/preview/d47255746d42a5a423037718ef8d8b62b5a692f0/packages/preview-shell/public/icons/apple-icon-180x180.png
--------------------------------------------------------------------------------
/packages/preview-shell/public/icons/apple-icon-57x57.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vuedx/preview/d47255746d42a5a423037718ef8d8b62b5a692f0/packages/preview-shell/public/icons/apple-icon-57x57.png
--------------------------------------------------------------------------------
/packages/preview-shell/public/icons/apple-icon-60x60.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vuedx/preview/d47255746d42a5a423037718ef8d8b62b5a692f0/packages/preview-shell/public/icons/apple-icon-60x60.png
--------------------------------------------------------------------------------
/packages/preview-shell/public/icons/apple-icon-72x72.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vuedx/preview/d47255746d42a5a423037718ef8d8b62b5a692f0/packages/preview-shell/public/icons/apple-icon-72x72.png
--------------------------------------------------------------------------------
/packages/preview-shell/public/icons/apple-icon-76x76.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vuedx/preview/d47255746d42a5a423037718ef8d8b62b5a692f0/packages/preview-shell/public/icons/apple-icon-76x76.png
--------------------------------------------------------------------------------
/packages/preview-shell/public/icons/apple-icon-precomposed.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vuedx/preview/d47255746d42a5a423037718ef8d8b62b5a692f0/packages/preview-shell/public/icons/apple-icon-precomposed.png
--------------------------------------------------------------------------------
/packages/preview-shell/public/icons/apple-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vuedx/preview/d47255746d42a5a423037718ef8d8b62b5a692f0/packages/preview-shell/public/icons/apple-icon.png
--------------------------------------------------------------------------------
/packages/preview-shell/public/icons/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vuedx/preview/d47255746d42a5a423037718ef8d8b62b5a692f0/packages/preview-shell/public/icons/favicon-16x16.png
--------------------------------------------------------------------------------
/packages/preview-shell/public/icons/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vuedx/preview/d47255746d42a5a423037718ef8d8b62b5a692f0/packages/preview-shell/public/icons/favicon-32x32.png
--------------------------------------------------------------------------------
/packages/preview-shell/public/icons/favicon-96x96.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vuedx/preview/d47255746d42a5a423037718ef8d8b62b5a692f0/packages/preview-shell/public/icons/favicon-96x96.png
--------------------------------------------------------------------------------
/packages/preview-shell/public/icons/ms-icon-144x144.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vuedx/preview/d47255746d42a5a423037718ef8d8b62b5a692f0/packages/preview-shell/public/icons/ms-icon-144x144.png
--------------------------------------------------------------------------------
/packages/preview-shell/public/icons/ms-icon-150x150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vuedx/preview/d47255746d42a5a423037718ef8d8b62b5a692f0/packages/preview-shell/public/icons/ms-icon-150x150.png
--------------------------------------------------------------------------------
/packages/preview-shell/public/icons/ms-icon-310x310.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vuedx/preview/d47255746d42a5a423037718ef8d8b62b5a692f0/packages/preview-shell/public/icons/ms-icon-310x310.png
--------------------------------------------------------------------------------
/packages/preview-shell/public/icons/ms-icon-70x70.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vuedx/preview/d47255746d42a5a423037718ef8d8b62b5a692f0/packages/preview-shell/public/icons/ms-icon-70x70.png
--------------------------------------------------------------------------------
/packages/preview-shell/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Preview",
3 | "icons": [
4 | {
5 | "src": "\/icons\/android-icon-36x36.png",
6 | "sizes": "36x36",
7 | "type": "image\/png",
8 | "density": "0.75"
9 | },
10 | {
11 | "src": "\/icons\/android-icon-48x48.png",
12 | "sizes": "48x48",
13 | "type": "image\/png",
14 | "density": "1.0"
15 | },
16 | {
17 | "src": "\/icons\/android-icon-72x72.png",
18 | "sizes": "72x72",
19 | "type": "image\/png",
20 | "density": "1.5"
21 | },
22 | {
23 | "src": "\/icons\/android-icon-96x96.png",
24 | "sizes": "96x96",
25 | "type": "image\/png",
26 | "density": "2.0"
27 | },
28 | {
29 | "src": "\/icons\/android-icon-144x144.png",
30 | "sizes": "144x144",
31 | "type": "image\/png",
32 | "density": "3.0"
33 | },
34 | {
35 | "src": "\/icons\/android-icon-192x192.png",
36 | "sizes": "192x192",
37 | "type": "image\/png",
38 | "density": "4.0"
39 | }
40 | ]
41 | }
42 |
--------------------------------------------------------------------------------
/packages/preview-shell/src/Root.vue:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
17 |
--------------------------------------------------------------------------------
/packages/preview-shell/src/app.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
--------------------------------------------------------------------------------
/packages/preview-shell/src/app.ts:
--------------------------------------------------------------------------------
1 | import './app.css';
2 | import { createApp } from 'vue';
3 | import Root from './Root.vue';
4 | import { router } from './router';
5 |
6 | Root.name = 'Preview';
7 | const app = createApp(Root);
8 |
9 | app.use(router);
10 |
11 | export { app };
12 |
--------------------------------------------------------------------------------
/packages/preview-shell/src/assets/MacBook-Pro-16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vuedx/preview/d47255746d42a5a423037718ef8d8b62b5a692f0/packages/preview-shell/src/assets/MacBook-Pro-16.png
--------------------------------------------------------------------------------
/packages/preview-shell/src/assets/assets.d.ts:
--------------------------------------------------------------------------------
1 | declare module '*.png' {
2 | const value: string;
3 | export default value;
4 | }
5 | declare module '*.svg' {
6 | const value: string;
7 | export default value;
8 | }
9 |
--------------------------------------------------------------------------------
/packages/preview-shell/src/assets/device-freeform.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/packages/preview-shell/src/assets/iPad-Pro-13-Landscape.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vuedx/preview/d47255746d42a5a423037718ef8d8b62b5a692f0/packages/preview-shell/src/assets/iPad-Pro-13-Landscape.png
--------------------------------------------------------------------------------
/packages/preview-shell/src/assets/iPad-Pro-13-Landscape.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/packages/preview-shell/src/assets/iPad-Pro-13-Portrait.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vuedx/preview/d47255746d42a5a423037718ef8d8b62b5a692f0/packages/preview-shell/src/assets/iPad-Pro-13-Portrait.png
--------------------------------------------------------------------------------
/packages/preview-shell/src/assets/iPad-Pro-13-Portrait.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/packages/preview-shell/src/assets/iPhone-11-Landscape.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vuedx/preview/d47255746d42a5a423037718ef8d8b62b5a692f0/packages/preview-shell/src/assets/iPhone-11-Landscape.png
--------------------------------------------------------------------------------
/packages/preview-shell/src/assets/iPhone-11-Landscape.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/packages/preview-shell/src/assets/iPhone-11-Portrait.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vuedx/preview/d47255746d42a5a423037718ef8d8b62b5a692f0/packages/preview-shell/src/assets/iPhone-11-Portrait.png
--------------------------------------------------------------------------------
/packages/preview-shell/src/assets/iPhone-11-Portrait.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/packages/preview-shell/src/components.ts:
--------------------------------------------------------------------------------
1 | import { ref } from 'vue';
2 |
3 | interface PreviewMetadata {
4 | id: number;
5 | name: string;
6 | device: string;
7 | deviceProps: Record;
8 | }
9 |
10 | export interface ComponentMetadata {
11 | id: string;
12 | name: string;
13 | path: string;
14 | previews: PreviewMetadata[];
15 | }
16 |
17 | export const components = ref((window as any).components ?? []);
18 |
19 | window.addEventListener('preview:components' as any, (event: CustomEvent) => {
20 | components.value = event.detail;
21 | });
22 |
23 | if (import.meta.env.DEV) {
24 | components.value = [
25 | {
26 | id: 'component/Alert',
27 | name: 'Alert',
28 | path: 'component/Alert.vue',
29 | previews: [
30 | { id: 1, name: 'One', device: 'freeform', deviceProps: { controls: true } },
31 | { id: 2, name: 'Two', device: 'mobile', deviceProps: {} },
32 | { id: 3, name: 'Three', device: 'desktop', deviceProps: {} },
33 | ],
34 | },
35 | {
36 | id: 'component/Button.vue',
37 | name: 'Button',
38 | path: 'component/in-a-very/very/very/very/deep/directory/Button.vue',
39 | previews: [
40 | { id: 1, name: 'One', device: 'freeform', deviceProps: { controls: true } },
41 | { id: 2, name: 'Two', device: 'mobile', deviceProps: {} },
42 | { id: 3, name: 'Three', device: 'desktop', deviceProps: {} },
43 | ],
44 | },
45 |
46 | ...Array(30)
47 | .fill(null)
48 | .map((_, i) => ({
49 | id: `component/Example${i}`,
50 | name: `Example${i}`,
51 | path: `components/${i}/Example${i}.vue`,
52 | previews: [
53 | { id: 1, name: 'One', device: 'freeform', deviceProps: { controls: true } },
54 | { id: 2, name: 'Two', device: 'mobile', deviceProps: {} },
55 | { id: 3, name: 'Three', device: 'desktop', deviceProps: {} },
56 | ] as PreviewMetadata[],
57 | })),
58 | ].sort(() => (Math.random() > 0.5 ? 1 : -1));
59 | }
60 |
--------------------------------------------------------------------------------
/packages/preview-shell/src/components/BaseDevice.vue:
--------------------------------------------------------------------------------
1 |
19 |
20 |
21 |
42 |
43 |
44 |
82 |
--------------------------------------------------------------------------------
/packages/preview-shell/src/components/Browser.vue:
--------------------------------------------------------------------------------
1 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
34 |
--------------------------------------------------------------------------------
/packages/preview-shell/src/components/ConfiguredDevice.vue:
--------------------------------------------------------------------------------
1 |
85 |
86 |
87 |
132 |
133 |
134 |
161 |
--------------------------------------------------------------------------------
/packages/preview-shell/src/components/Content.vue:
--------------------------------------------------------------------------------
1 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/packages/preview-shell/src/components/Device.vue:
--------------------------------------------------------------------------------
1 |
34 |
35 |
36 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/packages/preview-shell/src/components/DeviceSelector.vue:
--------------------------------------------------------------------------------
1 |
17 |
18 |
19 |
30 |
31 |
--------------------------------------------------------------------------------
/packages/preview-shell/src/components/Example.vue:
--------------------------------------------------------------------------------
1 |
2 | Example Component
3 |
4 |
--------------------------------------------------------------------------------
/packages/preview-shell/src/components/ExplorerComponents.vue:
--------------------------------------------------------------------------------
1 |
57 |
58 |
59 |
60 |
61 | Components
62 |
63 |
78 |
79 |
107 |
108 |
109 |
110 |
115 |
--------------------------------------------------------------------------------
/packages/preview-shell/src/components/FreeformDevice.vue:
--------------------------------------------------------------------------------
1 |
50 |
51 |
52 |
78 |
79 |
80 |
108 |
--------------------------------------------------------------------------------
/packages/preview-shell/src/components/Viewport.vue:
--------------------------------------------------------------------------------
1 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/packages/preview-shell/src/components/ZoomSelector.vue:
--------------------------------------------------------------------------------
1 |
22 |
23 |
24 |
28 |
29 |
--------------------------------------------------------------------------------
/packages/preview-shell/src/components/device-frame-hack.vue:
--------------------------------------------------------------------------------
1 |
2 |
19 |
20 |
--------------------------------------------------------------------------------
/packages/preview-shell/src/config.ts:
--------------------------------------------------------------------------------
1 | import { InjectionKey, Ref } from 'vue';
2 | import { ComponentModule } from './types';
3 |
4 | export const COMPONENTS: InjectionKey[> = Symbol.for('preview::components');
5 | export const ZOOM: InjectionKey][> = Symbol.for('preview::zoom');
6 | export const THEME: InjectionKey][> = Symbol.for('preview::theme');
7 |
--------------------------------------------------------------------------------
/packages/preview-shell/src/devices/index.ts:
--------------------------------------------------------------------------------
1 | import device_iPhone_11_Portrait from '../assets/iPhone-11-Portrait.png';
2 | import frame_iPhone_11_Portrait from '../assets/iPhone-11-Portrait.svg';
3 |
4 | import device_iPhone_11_Landscape from '../assets/iPhone-11-Landscape.png';
5 | import frame_iPhone_11_Landscape from '../assets/iPhone-11-Landscape.svg';
6 |
7 | import device_iPad_Pro_13_Portrait from '../assets/iPad-Pro-13-Portrait.png';
8 | import device_iPad_Pro_13_Landscape from '../assets/iPad-Pro-13-Landscape.png';
9 |
10 | import device_MacBook_Pro_16 from '../assets/MacBook-Pro-16.png';
11 |
12 | const isFirefox = /(firefox)/i.test(window.navigator.userAgent);
13 |
14 | export interface DeviceFrame {
15 | photo: string;
16 | mask?: string;
17 | }
18 |
19 | export interface DeviceSpecs {
20 | name: string;
21 | width: number;
22 | height: number;
23 | bezels: { top: number; right: number; bottom: number; left: number };
24 | frames: Record;
25 | }
26 |
27 | interface RawDeviceSpecs extends DeviceSpecs {
28 | aliases: string[];
29 | ratio: number;
30 | }
31 |
32 | export const devices: RawDeviceSpecs[] = [
33 | {
34 | name: 'iPhone 11',
35 | aliases: ['iPhone', 'iPhone 11', 'phone', 'mobile'],
36 | width: 828,
37 | height: 1792,
38 | ratio: 2,
39 | bezels: {
40 | top: 71,
41 | right: 76,
42 | bottom: 71,
43 | left: 75,
44 | },
45 | frames: {
46 | default: {
47 | photo: `url('${device_iPhone_11_Portrait}')`,
48 | mask: isFirefox
49 | ? `url('${frame_iPhone_11_Portrait}#mask')`
50 | : 'url("#device-iPhone-11-Portrait-mask")',
51 | },
52 | landscape: {
53 | photo: `url(${device_iPhone_11_Landscape})`,
54 | mask: isFirefox
55 | ? `url('${frame_iPhone_11_Landscape}#mask')`
56 | : 'url("#device-iPhone-11-Landscape-mask")',
57 | },
58 | },
59 | },
60 | {
61 | name: 'iPad Pro 12.9"',
62 | aliases: ['ipad', 'iPad', 'iPad Pro', 'tablet', 'tab'],
63 | width: 2048,
64 | height: 2732,
65 | ratio: 2,
66 | bezels: {
67 | top: 102,
68 | right: 101,
69 | bottom: 96,
70 | left: 96,
71 | },
72 | frames: {
73 | default: {
74 | photo: `url("${device_iPad_Pro_13_Portrait}")`,
75 | mask: 'inset(0 0 0 0 round 34px)',
76 | },
77 | landscape: {
78 | photo: `url("${device_iPad_Pro_13_Landscape}")`,
79 | mask: 'inset(0 0 0 0 round 34px)',
80 | },
81 | },
82 | },
83 | {
84 | name: 'MacBook Pro 16"',
85 | aliases: ['MacBook', 'MacBook Pro', 'Mac', 'laptop', 'mac', 'desktop'],
86 | width: 3072,
87 | height: 1920,
88 | ratio: 2,
89 | bezels: {
90 | top: 98,
91 | right: 419,
92 | bottom: 223,
93 | left: 419,
94 | },
95 | frames: {
96 | default: {
97 | photo: `url("${device_MacBook_Pro_16}")`,
98 | },
99 | },
100 | },
101 | ];
102 |
103 | export default toMap(devices);
104 |
105 | function toMap(devices: RawDeviceSpecs[]): Record {
106 | const map: Record = {};
107 |
108 | devices.forEach((device) => {
109 | const spec: DeviceSpecs = {
110 | name: device.name,
111 | width: device.width / device.ratio,
112 | height: device.height / device.ratio,
113 | bezels: {
114 | top: device.bezels.top / device.ratio,
115 | right: device.bezels.right / device.ratio,
116 | bottom: device.bezels.bottom / device.ratio,
117 | left: device.bezels.left / device.ratio,
118 | },
119 | frames: device.frames,
120 | };
121 |
122 | map[device.name] = spec;
123 | device.aliases.forEach((alias) => {
124 | if (!(alias in map)) {
125 | map[alias] = spec;
126 | }
127 | });
128 | });
129 |
130 | return map;
131 | }
132 |
--------------------------------------------------------------------------------
/packages/preview-shell/src/index.ts:
--------------------------------------------------------------------------------
1 | import { app } from './app';
2 |
3 | app.mount('#app');
4 |
5 |
--------------------------------------------------------------------------------
/packages/preview-shell/src/pages/dashboard.vue:
--------------------------------------------------------------------------------
1 |
42 |
43 |
44 | ]
45 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
109 |
110 |
111 |
112 |
113 |
118 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
141 |
147 |
148 |
149 |
150 |
151 |
152 |
165 | Select a component!
166 |
167 |
168 |
169 |
170 |
171 |
176 |
--------------------------------------------------------------------------------
/packages/preview-shell/src/pages/sandbox.vue:
--------------------------------------------------------------------------------
1 |
39 |
40 |
41 |
42 |
43 |
44 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
Select a component
57 |
58 |
59 |
60 |
73 |
--------------------------------------------------------------------------------
/packages/preview-shell/src/router.ts:
--------------------------------------------------------------------------------
1 | import { createRouter, createWebHistory } from 'vue-router';
2 |
3 | const isPluginMode = window.location.pathname.startsWith('/__preview');
4 |
5 | const router = createRouter({
6 | history: createWebHistory(isPluginMode ? '/__preview' : undefined),
7 | routes: [
8 | {
9 | name: 'dashboard',
10 | path: '/',
11 | component: (): any => import('./pages/dashboard.vue'),
12 | props: (route) => ({ fileName: route.query['fileName'] ?? route.query['filename'] }),
13 | },
14 | {
15 | name: 'sandbox',
16 | path: '/sandbox',
17 | component: (): any => import('./pages/sandbox.vue'),
18 | props: (route) => ({ fileName: route.query['fileName'] ?? route.query['filename'] }),
19 | },
20 | ],
21 | });
22 |
23 | export { router };
24 |
--------------------------------------------------------------------------------
/packages/preview-shell/src/utilities.ts:
--------------------------------------------------------------------------------
1 | export function getQueryParams(search = location.search) {
2 | const params: Record = {};
3 |
4 | search.split('&').forEach((param) => {
5 | const [name, value] = param.split('=');
6 |
7 | params[decodeURIComponent(name)] = value ? decodeURIComponent(value) : true;
8 | });
9 |
10 | return params;
11 | }
12 |
--------------------------------------------------------------------------------
/packages/preview-shell/tailwind.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | purge: ['./index.html', './src/**/*.{vue,js,ts,jsx,tsx}'],
3 | darkMode: false, // or 'media' or 'class'
4 | theme: {
5 | extend: {},
6 | },
7 | variants: {
8 | extend: {},
9 | },
10 | plugins: [],
11 | };
12 |
--------------------------------------------------------------------------------
/packages/preview-shell/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "include": ["src", "node_modules/vite/dist/importMeta.d.ts"],
4 | "compilerOptions": { "lib": ["ES2019", "DOM"] }
5 | }
6 |
--------------------------------------------------------------------------------
/packages/preview-shell/vite.config.js:
--------------------------------------------------------------------------------
1 | const vuePlugin = require('@vitejs/plugin-vue').default;
2 | const Path = require('path');
3 | const MagicString = require('magic-string').default;
4 |
5 | module.exports = /** @type {import('vite').UserConfig} */ ({
6 | plugins: [
7 | vuePlugin(),
8 | {
9 | transform(code, id) {
10 | if (id.endsWith('.vue')) {
11 | const magic = new MagicString(code);
12 |
13 | magic.append(
14 | `\n_sfc_main.__file = _sfc_main.__file ?? ${JSON.stringify(Path.basename(id))}\n`
15 | );
16 |
17 | return {
18 | code: magic.toString(),
19 | map: JSON.parse(magic.generateMap().toString()),
20 | };
21 | }
22 | },
23 | },
24 | ],
25 | css: {
26 | modules: false,
27 | },
28 | define: {
29 | __VUE_PROD_DEVTOOLS__: JSON.stringify(true),
30 | },
31 | });
32 |
--------------------------------------------------------------------------------
/packages/preview-test-utils/.npmignore:
--------------------------------------------------------------------------------
1 | dist/types/
2 |
--------------------------------------------------------------------------------
/packages/preview-test-utils/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # @vuedx/preview-test-utils
2 |
3 | ## 0.2.2
4 |
5 | ### Patch Changes
6 |
7 | - dd448e6: Support async imports in `` blocks
8 | - dd448e6: Sync color-scheme with VS Code
9 | - Updated dependencies [dd448e6]
10 | - Updated dependencies [dd448e6]
11 | - @vuedx/preview-compiler@0.2.2
12 | - @vuedx/preview-provider@0.1.5
13 |
14 | ## 0.2.1
15 |
16 | ### Patch Changes
17 |
18 | - b0eea6a: Build using @vuedx/monorepo-tools
19 | - Updated dependencies [b0eea6a]
20 | - @vuedx/preview-provider@0.1.4
21 | - @vuedx/preview-compiler@0.2.1
22 |
23 | ## 0.2.0
24 |
25 | ### Minor Changes
26 |
27 | - b03452d: Add HMR support
28 |
29 | ### Patch Changes
30 |
31 | - Updated dependencies [b03452d]
32 | - @vuedx/preview-compiler@0.2.0
33 |
34 | ## 0.1.3
35 |
36 | ### Patch Changes
37 |
38 | - 1b39181: Bump all versions to test continuous release CI
39 | - Updated dependencies [1b39181]
40 | - @vuedx/preview-compiler@0.1.3
41 | - @vuedx/preview-provider@0.1.3
42 |
43 | ## 0.1.2
44 |
45 | ### Patch Changes
46 |
47 | - 18a1b02: Bump version
48 | - Updated dependencies [18a1b02]
49 | - @vuedx/preview-compiler@0.1.2
50 | - @vuedx/preview-provider@0.1.2
51 |
52 | ## 0.1.1
53 |
54 | ### Patch Changes
55 |
56 | - e3ba132: Update HMR API
57 |
58 | - Version lock on vite@2.0.0-beta.44
59 | - Remove forked HMR client and use `/@vite/client` instead
60 |
61 | - e3ba132: Use `vue` instead of `@vue/runtime-core`
62 | - Updated dependencies [e3ba132]
63 | - Updated dependencies [e3ba132]
64 | - @vuedx/preview-compiler@0.1.1
65 | - @vuedx/preview-provider@0.1.1
66 |
67 | ## 0.1.0
68 |
69 | ### Minor Changes
70 |
71 | - d34a262: VS Code preview extension
72 |
73 | ### Patch Changes
74 |
75 | - Updated dependencies [d34a262]
76 | - @vuedx/preview-compiler@0.1.0
77 | - @vuedx/preview-provider@0.1.0
78 |
--------------------------------------------------------------------------------
/packages/preview-test-utils/jest.config.js:
--------------------------------------------------------------------------------
1 | const Path = require('path');
2 | const { name } = require('./package.json');
3 |
4 | const TS_CONFIG = Path.resolve(__dirname, 'tsconfig.json');
5 | const SETUP_FILE = Path.resolve(__dirname, './test/setup.ts');
6 |
7 | /** @type {import('@jest/types').Config.InitialOptions} */
8 | const config = {
9 | displayName: {
10 | name,
11 | color: 'red',
12 | },
13 | verbose: true,
14 | transform: {
15 | '^.+\\.js$': 'babel-jest',
16 | '^.+\\.ts$': 'ts-jest',
17 | '^.+\\.vue$': 'vue-jest',
18 | },
19 | setupFiles: [SETUP_FILE],
20 | globals: {
21 | 'ts-jest': {
22 | tsconfig: TS_CONFIG,
23 | isolatedModules: true,
24 | },
25 | 'vue-jest': {
26 | tsConfig: TS_CONFIG,
27 | },
28 | },
29 | };
30 |
31 | module.exports = config;
32 |
--------------------------------------------------------------------------------
/packages/preview-test-utils/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@vuedx/preview-test-utils",
3 | "version": "0.2.2",
4 | "main": "dist/preview-test-utils.js",
5 | "module": "dist/preview-test-utils.mjs",
6 | "types": "src/index.ts",
7 | "publishConfig": {
8 | "types": "dist/preview-test-utils.d.ts"
9 | },
10 | "buildConfig": {},
11 | "dependencies": {
12 | "@babel/core": "^7.12.10",
13 | "@babel/plugin-syntax-dynamic-import": "^7.8.3",
14 | "@babel/plugin-transform-modules-commonjs": "^7.12.1",
15 | "@babel/preset-env": "^7.14.5",
16 | "@vuedx/compiler-sfc": "^0.7.1",
17 | "@vuedx/preview-compiler": "workspace:*",
18 | "@vuedx/preview-provider": "workspace:*",
19 | "babel-preset-jest": "^26.6.2",
20 | "override-require": "^1.1.1"
21 | },
22 | "devDependencies": {
23 | "@testing-library/vue": "^6.4.2",
24 | "@types/jest": "^26.0.23",
25 | "@types/node": "^14.14.22",
26 | "@vue/compiler-sfc": "^3.1.1",
27 | "babel-jest": "^26.6.3",
28 | "jest": "^26.6.3",
29 | "ts-jest": "^26.5.6",
30 | "typescript": "^4.1.3",
31 | "vue": "^3.1.1",
32 | "vue-jest": "^5.0.0-alpha.10",
33 | "whatwg-fetch": "^3.6.2"
34 | },
35 | "peerDependencies": {
36 | "vue": "^3.0.5"
37 | },
38 | "peerDependenciesMeta": {
39 | "vue": {
40 | "optional": true
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/packages/preview-test-utils/placeholder.js:
--------------------------------------------------------------------------------
1 | throw new Error(
2 | `preview:for-test file should be processed with jest transformer: "@vuedx/preview-test-utils"`
3 | );
4 |
--------------------------------------------------------------------------------
/packages/preview-test-utils/src/index.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore - rollup-plugin-dts does not support export *
2 | import type { ComponentPublicInstance, App } from 'vue';
3 | import type { SetupOptions } from '@vuedx/preview-provider';
4 | import * as Path from 'path';
5 | import { getStackTraceLine } from './stacktrace';
6 | import { execute, executeAsApp } from './vm';
7 |
8 | /**
9 | * Load block as a component.
10 | *
11 | * The component SFC/.vue file is detected using the test
12 | * file name, e.g.:
13 | * - foo.ts => foo.vue
14 | * - foo.spec.ts => foo.vue
15 | * - foo.test.ts => foo.vue
16 | * - src/__tests__/foo.spec.ts => src/foo.vue
17 | * @param previewName "name" attribute of the required block
18 | */
19 | export function usePreview(previewName: string): ComponentPublicInstance;
20 |
21 | /**
22 | * Load block as a component.
23 | *
24 | * The component SFC/.vue file is detected using the test
25 | * file name, e.g.:
26 | * - foo.ts => foo.vue
27 | * - foo.spec.ts => foo.vue
28 | * - foo.test.ts => foo.vue
29 | * - src/__tests__/foo.spec.ts => src/foo.vue
30 | * @param previewName "name" attribute of the required block
31 | * @param overrides Override mocks and stubs set in block
32 | */
33 | export function usePreview(
34 | previewName: string,
35 | overrides: Partial
36 | ): ComponentPublicInstance;
37 |
38 | /**
39 | * Load block as a component.
40 | *
41 | * @param fileName The component SFC/.vue file.
42 | * @param previewName "name" attribute of the required block
43 | */
44 | export function usePreview(fileName: string, previewName: string): ComponentPublicInstance;
45 |
46 | /**
47 | * Load block as a component.
48 | *
49 | * @param fileName The component SFC/.vue file.
50 | * @param previewName "name" attribute of the required block
51 | * @param overrides Override mocks and stubs set in block
52 | */
53 | export function usePreview(
54 | fileName: string,
55 | previewName: string,
56 | overrides: Partial
57 | ): ComponentPublicInstance;
58 |
59 | export function usePreview(
60 | ...args:
61 | | [string]
62 | | [string, Partial | string]
63 | | [string, string, Partial]
64 | ): ComponentPublicInstance {
65 | const { fileName, previewName, overrides } = prepareArgs(args);
66 |
67 | return execute(fileName, previewName)(overrides);
68 | }
69 |
70 | /**
71 | * Load block as an app.
72 | *
73 | * The component SFC/.vue file is detected using the test
74 | * file name, e.g.:
75 | * - foo.ts => foo.vue
76 | * - foo.spec.ts => foo.vue
77 | * - foo.test.ts => foo.vue
78 | * - src/__tests__/foo.spec.ts => src/foo.vue
79 | * @param previewName "name" attribute of the required block
80 | */
81 | export function usePreviewApp(previewName: string): App;
82 |
83 | /**
84 | * Load block as an app.
85 | *
86 | * The component SFC/.vue file is detected using the test
87 | * file name, e.g.:
88 | * - foo.ts => foo.vue
89 | * - foo.spec.ts => foo.vue
90 | * - foo.test.ts => foo.vue
91 | * - src/__tests__/foo.spec.ts => src/foo.vue
92 | * @param previewName "name" attribute of the required block
93 | * @param overrides Override mocks and stubs set in block
94 | */
95 | export function usePreviewApp(previewName: string, overrides: Partial): App;
96 |
97 | /**
98 | * Load block as an app.
99 | *
100 | * @param fileName The component SFC/.vue file.
101 | * @param previewName "name" attribute of the required block
102 | */
103 | export function usePreviewApp(fileName: string, previewName: string): App;
104 |
105 | /**
106 | * Load block as an app.
107 | *
108 | * @param fileName The component SFC/.vue file.
109 | * @param previewName "name" attribute of the required block
110 | * @param overrides Override mocks and stubs set in block
111 | */
112 | export function usePreviewApp(
113 | fileName: string,
114 | previewName: string,
115 | overrides: Partial
116 | ): App;
117 |
118 | export function usePreviewApp(
119 | ...args:
120 | | [string]
121 | | [string, Partial | string]
122 | | [string, string, Partial]
123 | ): App {
124 | const { fileName, previewName, overrides } = prepareArgs(args);
125 |
126 | return executeAsApp(fileName, previewName)(overrides);
127 | }
128 |
129 | function prepareArgs(
130 | args:
131 | | [string]
132 | | [string, string | Partial>]
133 | | [string, string, Partial>]
134 | ): { fileName: string; previewName: string; overrides: Partial } {
135 | const fileName = getFileName(
136 | args.length === 3 || (args.length === 2 && typeof args[1] === 'string') ? args[0] : undefined,
137 | 3
138 | );
139 |
140 | const previewName =
141 | args.length === 3
142 | ? args[1]
143 | : args.length === 2 && typeof args[1] === 'string'
144 | ? args[1]
145 | : args[0];
146 | const overrides =
147 | args.length === 3 ? args[2] : args.length === 2 && typeof args[1] !== 'string' ? args[1] : {};
148 |
149 | return { fileName, previewName, overrides };
150 | }
151 |
152 | function getFileName(fileName?: string, depth: number = 2): string {
153 | if (fileName != null && Path.isAbsolute(fileName)) return fileName;
154 | const trace = getStackTraceLine(depth);
155 | if (trace == null) throw new Error('CannotDetectFileName');
156 | if (fileName != null) return Path.resolve(Path.dirname(trace.fileName), fileName);
157 | const dirName = Path.dirname(trace.fileName);
158 | const baseName =
159 | Path.basename(trace.fileName).replace(/(?:\.(?:spec|test))?\.(?:js|ts)x?$/, '') + '.vue';
160 |
161 | const testDir = Path.sep + '__tests__' + Path.sep;
162 | if (dirName.includes(testDir)) {
163 | return Path.resolve(dirName.replace(testDir, Path.sep), baseName);
164 | }
165 |
166 | return Path.resolve(dirName, baseName);
167 | }
168 |
--------------------------------------------------------------------------------
/packages/preview-test-utils/src/stacktrace.ts:
--------------------------------------------------------------------------------
1 | const STACK_LINE_RE =
2 | /^\s*at (?:((?:\[object object\])?[^\\/]+(?: \[as \S+\])?) )?\(?(.*?):(\d+)(?::(\d+))?\)?\s*$/i;
3 | interface StackTraceLine {
4 | fileName: string;
5 | methodName: string;
6 | arguments: string[];
7 | line: number;
8 | column?: number;
9 | }
10 | export function getStackTraceLine(depth: number = 0): StackTraceLine | undefined {
11 | const error = new Error();
12 | const lines = (error.stack ?? '').split('\n').map(parseStackTrace).filter(Boolean);
13 | return lines[depth + 1];
14 | }
15 |
16 | function parseStackTrace(trace: string): StackTraceLine | undefined {
17 | const parts = STACK_LINE_RE.exec(trace);
18 | if (!parts) return;
19 | return {
20 | fileName: String(parts[2]),
21 | methodName: parts[1] ?? '',
22 | arguments: [],
23 | line: parts[3] != null ? parseInt(parts[3]) : 0,
24 | column: parts[4] != null ? parseInt(parts[4]) : undefined,
25 | };
26 | }
27 |
--------------------------------------------------------------------------------
/packages/preview-test-utils/src/transform.ts:
--------------------------------------------------------------------------------
1 | import { transformSync as transform } from '@babel/core';
2 | import { parse, SFCDescriptor } from '@vuedx/compiler-sfc';
3 | import { compile } from '@vuedx/preview-compiler';
4 | import * as FS from 'fs';
5 | import * as Path from 'path';
6 |
7 | class DescriptorStore {
8 | private readonly cache = new Map();
9 |
10 | private readonly prevContent = new Map();
11 |
12 | get(fileName: string, content?: string): SFCDescriptor {
13 | const descriptor = this.cache.get(fileName);
14 | if (descriptor == null) {
15 | return this.set(fileName, content ?? FS.readFileSync(fileName, 'utf-8'));
16 | } else if (content != null) {
17 | return this.set(fileName, content);
18 | }
19 |
20 | return descriptor;
21 | }
22 |
23 | set(fileName: string, content: string): SFCDescriptor {
24 | const prevContent = this.prevContent.get(fileName);
25 | const descriptor = this.cache.get(fileName);
26 | if (prevContent !== content || descriptor == null) {
27 | this.prevContent.set(fileName, content);
28 | const { descriptor } = parse(content, { filename: fileName, sourceMap: false });
29 | this.cache.set(fileName, descriptor);
30 | return descriptor;
31 | } else {
32 | return descriptor;
33 | }
34 | }
35 | }
36 | const store = new DescriptorStore();
37 |
38 | const configCache: Array<[string, string]> = [];
39 | export function findPreviewConfig(fileName: string): string | undefined {
40 | const config = configCache.find(([dir]) => fileName.startsWith(dir));
41 | if (config != null) return config[1];
42 | let dirName = Path.dirname(fileName);
43 | while (dirName !== Path.dirname(dirName)) {
44 | const configs = [Path.resolve(dirName, 'preview.ts'), Path.resolve(dirName, 'preview.js')];
45 | const configFile = configs.find((fileName) => FS.existsSync(fileName));
46 | if (configFile != null) {
47 | configCache.push([dirName, configFile]);
48 | return configFile;
49 | }
50 |
51 | dirName = Path.dirname(dirName);
52 | }
53 |
54 | return undefined;
55 | }
56 | export function getPreviewId(
57 | fileName: string,
58 | previewName: string,
59 | isApp: boolean = false
60 | ): string {
61 | return `${fileName}?${isApp ? 'app&' : ''}preview=${encodeURIComponent(previewName).replace(
62 | /%20/g,
63 | '+'
64 | )}`;
65 | }
66 |
67 | export function generatePreviewComponent(
68 | fileName: string,
69 | previewName: string,
70 | isAppMode: boolean = false
71 | ): string {
72 | const descriptor = store.get(fileName);
73 | const block = descriptor.customBlocks.find(
74 | (block) => block.type === 'preview' && block.attrs['name'] === previewName
75 | );
76 | if (block == null) throw new Error(`Cannot find preview: "${previewName}" in ${fileName}`);
77 |
78 | let preamble = '';
79 |
80 | if (isAppMode) {
81 | const configFile = findPreviewConfig(fileName);
82 | if (configFile != null) {
83 | const importSource = configFile.replace(/\.(ts|js)$/, '');
84 | preamble = [
85 | `import * as _preview from '${importSource}'`,
86 | `import * as _vue from 'vue'`,
87 | `function _createApp(component, attrs) {`,
88 | ` const createApp = typeof _preview.createApp === 'function'`,
89 | ` ? _preview.createApp`,
90 | ` : _vue.createApp`,
91 | ` const app = createApp(component, attrs)`,
92 | ` if (_preview.x != null) {`,
93 | ` app.provide('preview:UserProviders', _preview.x)`,
94 | ` }`,
95 | ` return app`,
96 | `}`,
97 | ``,
98 | ].join('\n');
99 | } else {
100 | preamble = `import { createApp as _createApp } from 'vue'\n`;
101 | }
102 | }
103 |
104 | const id = getPreviewId(fileName, previewName, isAppMode);
105 | const code =
106 | preamble +
107 | compile(block.content, {
108 | componentFileName: fileName,
109 | allowOverrides: isAppMode ? '_createApp' : true,
110 | attrs: block.attrs,
111 | imports: {
112 | '@vuedx/preview-provider': require.resolve('@vuedx/preview-provider').replace(/\\/g, '/'),
113 | },
114 | });
115 |
116 | const result = transform(code, {
117 | filename: id,
118 | sourceFileName: fileName,
119 | presets: [
120 | [
121 | require.resolve('@babel/preset-env'),
122 | {
123 | targets: {
124 | node: 'current',
125 | },
126 | },
127 | ],
128 | ],
129 | babelrc: false,
130 | caller: {
131 | name: '@vuedx/preview',
132 | supportsStaticESM: false,
133 | supportsDynamicImport: false,
134 | supportsTopLevelAwait: false,
135 | supportsExportNamespaceFrom: false,
136 | },
137 | sourceMaps: 'inline',
138 | sourceType: 'module',
139 | generatorOpts: {
140 | filename: id,
141 | sourceFileName: fileName,
142 | sourceMaps: true,
143 | },
144 | });
145 |
146 | if (result?.code == null) {
147 | console.warn(`Babel transform failed for:\n${code}`);
148 |
149 | throw new Error(`Babel tranform failed in ${id} (@vuedx/preview)`);
150 | }
151 |
152 | return result.code;
153 | }
154 |
--------------------------------------------------------------------------------
/packages/preview-test-utils/src/vm.ts:
--------------------------------------------------------------------------------
1 | import type { SetupOptions } from '@vuedx/preview-provider';
2 | import { Module } from 'module';
3 | import * as Path from 'path';
4 | import * as vm from 'vm';
5 | import type { App, ComponentPublicInstance } from 'vue';
6 | import { generatePreviewComponent, getPreviewId } from './transform';
7 |
8 | type PreviewFactory = (overrides: Partial) => T;
9 | type PreviewFactoryModule = (
10 | exports: any,
11 | require: NodeJS.Require,
12 | module: NodeModule,
13 | fileName: string,
14 | dirName: string
15 | ) => PreviewFactory;
16 |
17 | const compilations: Record = {};
18 | function compile(fileName: string, previewName: string): PreviewFactoryModule {
19 | const id = getPreviewId(fileName, previewName, false);
20 | if (compilations[id] != null) return compilations[id];
21 |
22 | const code = generatePreviewComponent(fileName, previewName, false);
23 |
24 | compilations[id] = vm.runInThisContext(Module.wrap(code), {
25 | filename: id,
26 | displayErrors: true,
27 | microtaskMode: 'afterEvaluate',
28 | });
29 |
30 | return compilations[id];
31 | }
32 |
33 | function compileAsApp(fileName: string, previewName: string): PreviewFactoryModule {
34 | const id = `${fileName}?app&preview=${encodeURIComponent(previewName).replace(/%20/g, '+')}`;
35 | if (compilations[id] != null) return compilations[id];
36 |
37 | const code = generatePreviewComponent(fileName, previewName, true);
38 | compilations[id] = vm.runInThisContext(Module.wrap(code), {
39 | filename: id,
40 | displayErrors: true,
41 | microtaskMode: 'afterEvaluate',
42 | });
43 |
44 | return compilations[id];
45 | }
46 |
47 | const executions: Record> = {};
48 | interface RunOptions {
49 | fileName: string;
50 | previewName: string;
51 | isAppMode: boolean;
52 | }
53 |
54 | function run({ fileName, previewName, isAppMode }: RunOptions): PreviewFactory {
55 | // FIXME: Invalidate for watch mode.
56 | const id = getPreviewId(fileName, previewName, isAppMode);
57 | if (executions[id] != null) return executions[id] as PreviewFactory;
58 |
59 | const fn: PreviewFactoryModule = isAppMode
60 | ? compileAsApp(fileName, previewName)
61 | : compile(fileName, previewName);
62 |
63 | const req = Module.createRequire(fileName);
64 |
65 | // function req(id: string): any {
66 | // if (id === '@vuedx/preview-provider') {
67 | // try {
68 | // return require(id);
69 | // } catch (error) {
70 | // console.log(error);
71 | // }
72 | // }
73 |
74 | // return r(id);
75 | // }
76 |
77 | // req.resolve = r.resolve;
78 | // req.cache = r.cache;
79 | // req.main = r.main;
80 | // req.extensions = r.extensions;
81 | const m: typeof module = {
82 | id: id,
83 | exports: {},
84 | children: [],
85 | require: req,
86 | filename: id,
87 | parent: module,
88 | path: Path.dirname(fileName),
89 | paths: req.resolve.paths(Path.dirname(fileName)) ?? [],
90 | loaded: false,
91 | };
92 |
93 | fn.call(m, m.exports, req, m, fileName, Path.dirname(fileName));
94 |
95 | m.loaded = true;
96 |
97 | return (executions[id] = m.exports.default);
98 | }
99 |
100 | export function execute(fileName: string, previewName: string): PreviewFactory {
101 | return run({ fileName, previewName, isAppMode: false });
102 | }
103 |
104 | export function executeAsApp(fileName: string, previewName: string): PreviewFactory {
105 | return run({ fileName, previewName, isAppMode: true });
106 | }
107 |
--------------------------------------------------------------------------------
/packages/preview-test-utils/test/fixtures/Example.vue:
--------------------------------------------------------------------------------
1 |
2 | Example
3 |
4 |
--------------------------------------------------------------------------------
/packages/preview-test-utils/test/fixtures/MockComponent.vue:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 | Component:
7 |
8 |
--------------------------------------------------------------------------------
/packages/preview-test-utils/test/fixtures/MockFetchRequest.vue:
--------------------------------------------------------------------------------
1 |
11 |
12 |
13 | Hello {{ foo }}
14 |
15 |
16 |
17 |
22 |
23 |
24 |
25 |
26 |
27 |
32 |
33 |
34 |
35 |
36 |
37 |
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/packages/preview-test-utils/test/fixtures/api/foo.json:
--------------------------------------------------------------------------------
1 | {
2 | "foo": "Everyone"
3 | }
4 |
--------------------------------------------------------------------------------
/packages/preview-test-utils/test/setup.ts:
--------------------------------------------------------------------------------
1 | import 'whatwg-fetch'
2 |
--------------------------------------------------------------------------------
/packages/preview-test-utils/test/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "include": ["../src/", "*.ts"],
4 | "compilerOptions": {
5 | "types": ["jest", "node"]
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/packages/preview-test-utils/test/usePreview.spec.ts:
--------------------------------------------------------------------------------
1 | import { usePreview } from '../src';
2 | import { render } from '@testing-library/vue';
3 |
4 | describe('usePreview()', () => {
5 | describe('fetch intercetpion', () => {
6 | it('should use local value', async () => {
7 | const { findByText } = render(
8 | usePreview('./fixtures/MockFetchRequest.vue', 'greets world')
9 | );
10 | const el = await findByText('Hello World');
11 | expect(el).toBeTruthy();
12 | });
13 |
14 | it('should use imported value', async () => {
15 | const { findByText } = render(
16 | usePreview('./fixtures/MockFetchRequest.vue', 'greets everyone')
17 | );
18 | const el = await findByText('Hello Everyone');
19 | expect(el).toBeTruthy();
20 | });
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/packages/preview-test-utils/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "include": ["src"],
4 | "compilerOptions": { "lib": ["ES2019", "DOM"] }
5 | }
6 |
--------------------------------------------------------------------------------
/packages/preview/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # @vuedx/preview
2 |
3 | ## 0.3.0
4 |
5 | ### Minor Changes
6 |
7 | - ff930e3: Use `/__preview` path prefix for plugin mode
8 |
9 | BREAKING CHANGE: Internal virtual files also use `__preview` prefix, instead of `@preview`.
10 |
11 | ### Patch Changes
12 |
13 | - Updated dependencies [ff930e3]
14 | - @vuedx/preview-shell@0.3.0
15 |
16 | ## 0.2.4
17 |
18 | ### Patch Changes
19 |
20 | - cc16fc4: Ignore failures in metadata extraction from static code analysis
21 |
22 | ## 0.2.3
23 |
24 | ### Patch Changes
25 |
26 | - 70730fb: Ignore errors in static code analysis
27 |
28 | ## 0.2.2
29 |
30 | ### Patch Changes
31 |
32 | - dd448e6: Support async imports in `` blocks
33 | - dd448e6: Sync color-scheme with VS Code
34 | - Updated dependencies [dd448e6]
35 | - Updated dependencies [dd448e6]
36 | - @vuedx/preview-compiler@0.2.2
37 | - @vuedx/preview-provider@0.1.5
38 | - @vuedx/preview-shell@0.2.2
39 |
40 | ## 0.2.1
41 |
42 | ### Patch Changes
43 |
44 | - b0eea6a: Build using @vuedx/monorepo-tools
45 | - Updated dependencies [b0eea6a]
46 | - @vuedx/preview-provider@0.1.4
47 | - @vuedx/preview-shell@0.2.1
48 | - @vuedx/preview-compiler@0.2.1
49 |
50 | ## 0.2.0
51 |
52 | ### Minor Changes
53 |
54 | - b03452d: Add HMR support
55 |
56 | ### Patch Changes
57 |
58 | - Updated dependencies [b03452d]
59 | - @vuedx/preview-compiler@0.2.0
60 | - @vuedx/preview-shell@0.2.0
61 |
62 | ## 0.1.6
63 |
64 | ### Patch Changes
65 |
66 | - Updated dependencies [83e89e5]
67 | - @vuedx/preview-shell@0.1.4
68 |
69 | ## 0.1.5
70 |
71 | ### Patch Changes
72 |
73 | - 1b39181: Bump all versions to test continuous release CI
74 | - Updated dependencies [1b39181]
75 | - @vuedx/preview-compiler@0.1.3
76 | - @vuedx/preview-provider@0.1.3
77 | - @vuedx/preview-shell@0.1.3
78 |
79 | ## 0.1.4
80 |
81 | ### Patch Changes
82 |
83 | - 387878f: Update vite to version 2.x
84 |
85 | ## 0.1.3
86 |
87 | ### Patch Changes
88 |
89 | - 18a1b02: Bump version
90 | - Updated dependencies [18a1b02]
91 | - Updated dependencies [18a1b02]
92 | - @vuedx/preview-shell@0.1.2
93 | - @vuedx/preview-compiler@0.1.2
94 | - @vuedx/preview-provider@0.1.2
95 |
96 | ## 0.1.2
97 |
98 | ### Patch Changes
99 |
100 | - e3ba132: Update HMR API
101 |
102 | - Version lock on vite@2.0.0-beta.44
103 | - Remove forked HMR client and use `/@vite/client` instead
104 |
105 | - Updated dependencies [e3ba132]
106 | - Updated dependencies [e3ba132]
107 | - @vuedx/preview-compiler@0.1.1
108 | - @vuedx/preview-provider@0.1.1
109 | - @vuedx/preview-shell@0.1.1
110 |
111 | ## 0.1.1
112 |
113 | ### Patch Changes
114 |
115 | - 03225aa: Update vite HMR API
116 |
117 | ## 0.1.0
118 |
119 | ### Minor Changes
120 |
121 | - d34a262: VS Code preview extension
122 |
123 | ### Patch Changes
124 |
125 | - Updated dependencies [d34a262]
126 | - @vuedx/preview-compiler@0.1.0
127 | - @vuedx/preview-provider@0.1.0
128 | - @vuedx/preview-shell@0.1.0
129 |
--------------------------------------------------------------------------------
/packages/preview/bin/preview.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | const argv = require('minimist')(process.argv.slice(2));
4 | // make sure to set debug flag before requiring anything
5 | if (argv.debug) {
6 | process.env.DEBUG = `@vuedx/preview:` + (argv.debug === true ? '*' : argv.debug);
7 | try {
8 | // this is only present during local development
9 | require('source-map-support').install();
10 | } catch (e) {}
11 | }
12 |
13 | require('../dist/cli').run(argv);
14 |
--------------------------------------------------------------------------------
/packages/preview/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@vuedx/preview",
3 | "version": "0.3.0",
4 | "description": "Component preview tool for rapid development.",
5 | "main": "dist/preview.js",
6 | "module": "dist/preview.mjs",
7 | "types": "srs/plugin.ts",
8 | "bin": "bin/preview.js",
9 | "publishConfig": {
10 | "types": "dist/preview.d.ts"
11 | },
12 | "buildConfig": {
13 | "sources": {
14 | "src/cli.ts": [
15 | {
16 | "format": "commonjs",
17 | "file": "dist/cli.js"
18 | }
19 | ]
20 | }
21 | },
22 | "files": [
23 | "bin",
24 | "dist"
25 | ],
26 | "scripts": {},
27 | "keywords": [
28 | "vue",
29 | "storybook",
30 | "styleguide"
31 | ],
32 | "author": "Rahul Kadyan ",
33 | "license": "MIT",
34 | "devDependencies": {
35 | "@rollup/plugin-json": "^4.1.0",
36 | "@types/debug": "^4.1.5",
37 | "@types/etag": "^1.8.1",
38 | "@vuedx/analyze": "^0.6.0",
39 | "typescript": "^4.1.3",
40 | "vue": "^3.1.1"
41 | },
42 | "dependencies": {
43 | "@vitejs/plugin-vue": "^1.2.3",
44 | "@vue/compiler-sfc": "^3.1.1",
45 | "@vuedx/compiler-sfc": "^0.6.0",
46 | "@vuedx/preview-compiler": "workspace:*",
47 | "@vuedx/preview-provider": "workspace:*",
48 | "@vuedx/preview-shell": "workspace:*",
49 | "chalk": "^4.1.0",
50 | "debug": "^4.3.1",
51 | "etag": "^1.8.1",
52 | "fast-glob": "^3.2.5",
53 | "minimist": "^1.2.5",
54 | "picomatch": "^2.2.2",
55 | "quick-lru": "^6.0.0",
56 | "sirv": "^1.0.10",
57 | "vite": "^2.8.4"
58 | },
59 | "peerDependencies": {
60 | "@vuedx/analyze": "^0.6.0"
61 | },
62 | "peerDependenciesMeta": {
63 | "@vuedx/analyze": {
64 | "optional": true
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/packages/preview/src/cli.ts:
--------------------------------------------------------------------------------
1 | import chalk from 'chalk';
2 | import * as Path from 'path';
3 | import * as FS from 'fs';
4 | import { createServer } from 'vite';
5 | import { PreviewPlugin } from './index';
6 | import VuePlugin from '@vitejs/plugin-vue';
7 |
8 | function logHelp(): void {
9 | console.log(`
10 | Usage: preview [command] [args] [--options]
11 | Commands:
12 | preview Start server in current directory.
13 | preview serve [root=cwd] Start server in target directory.
14 | Options:
15 | --help, -h [boolean] show help
16 | --version, -v [boolean] show version
17 | --force, -f [boolean] force dependency optimization
18 | --config, -c [string] use specified config file
19 | --port [number] port to use for serve
20 | --open [boolean] open browser on server start
21 | --base [string] public base path for build (default: /)
22 | `);
23 | }
24 |
25 | export async function run(argv: {
26 | help: boolean;
27 | h: boolean;
28 | version: boolean;
29 | v: boolean;
30 | _: string[];
31 | }): Promise {
32 | if (argv._[0] != null && !/^(build|serve)$/i.test(argv._[0])) {
33 | argv._[1] = argv._[0];
34 | argv._[0] = 'serve';
35 | }
36 |
37 | const command = argv._[0];
38 |
39 | const { version: previewVersion } = JSON.parse(
40 | FS.readFileSync(require.resolve('../package.json'), 'utf-8')
41 | );
42 | const { version: viteVersion } = JSON.parse(
43 | FS.readFileSync(require.resolve('vite/package.json'), 'utf-8')
44 | );
45 |
46 | console.log(chalk.cyan(`Preview v${String(previewVersion)} (vite v${String(viteVersion)})`));
47 | const { help, h, version, v } = argv;
48 |
49 | if (help || h) {
50 | logHelp();
51 | return;
52 | } else if (version || v) {
53 | return;
54 | }
55 |
56 | if (command == null || command === 'serve') {
57 | await runServe(resolveOptions('development', argv));
58 | } else if (command === 'build') {
59 | throw new Error(`'preview build' is not implemented`);
60 | } else {
61 | console.error(`Unknown command: ${command}`);
62 | logHelp();
63 |
64 | process.exit(127);
65 | }
66 | }
67 |
68 | function resolveOptions(
69 | mode: string,
70 | argv: { root?: string; open?: boolean; base?: string; mode?: string; port?: string; _: string[] }
71 | ): ServeArgs {
72 | const options: ServeArgs = {
73 | mode,
74 | port: 3000,
75 | open: false,
76 | root: process.cwd(),
77 | };
78 |
79 | if (argv.base != null) options.base = argv.base;
80 | if (argv.mode != null) options.mode = argv.mode;
81 | if (argv.port != null) options.port = parseInt(argv.port);
82 | if (argv.open != null) options.open = true;
83 |
84 | // normalize root
85 | // assumes all commands are in the form of `preview [command] [root]`
86 | if (argv.root == null && argv._[1] != null) {
87 | options.root = argv._[1];
88 | }
89 |
90 | options.root = Path.isAbsolute(options.root) ? options.root : Path.resolve(options.root);
91 |
92 | return options;
93 | }
94 |
95 | interface ServeArgs {
96 | mode: string;
97 | port: number;
98 | open: boolean;
99 | root: string;
100 | base?: string;
101 | config?: string;
102 | force?: boolean;
103 | }
104 |
105 | async function runServe(options: ServeArgs): Promise {
106 | const hasViteConfig =
107 | FS.existsSync(Path.resolve(options.root, 'vite.config.js')) ||
108 | FS.existsSync(Path.resolve(options.root, 'vite.config.ts'));
109 |
110 | const server = await createServer({
111 | root: options.root,
112 | mode: options.mode,
113 | logLevel: 'info',
114 | server: {
115 | port: options.port,
116 | open: options.open,
117 | force: options.force,
118 | },
119 | plugins: hasViteConfig ? PreviewPlugin() : [VuePlugin(), ...PreviewPlugin()],
120 | configFile: options.config,
121 | });
122 |
123 | // TODO: Use a better logger
124 | console.log(`Working Dir: ${options.root}`);
125 |
126 | await server.listen(options.port);
127 | }
128 |
--------------------------------------------------------------------------------
/packages/preview/src/generators.ts:
--------------------------------------------------------------------------------
1 | import * as FS from 'fs';
2 | import * as Path from 'path';
3 | import type { ExtendedSFCDescriptor } from './store/DescriptorStore';
4 | import { ComponentResourceType, resourceToID, ResourceType } from './virtual-resource';
5 |
6 | export function genPreviewIFrameContent(
7 | rootDir: string,
8 | resource: {
9 | fileName: string;
10 | index?: number | undefined;
11 | }
12 | ): string {
13 | const fileName = Path.relative(rootDir, resource.fileName);
14 | const app = resourceToID({
15 | type: ComponentResourceType.ENTRY,
16 | fileName: fileName,
17 | index: resource.index,
18 | });
19 |
20 | return [
21 | ``,
22 | ``,
23 | ``,
24 | ` `,
25 | ` `,
26 | ` Preview of ${fileName}`,
27 | ``,
28 | ``,
29 | ` `,
30 | `
Loading
`,
31 | `
`,
32 | ` `,
33 | ` `,
34 | ``,
35 | ``,
36 | ].join('\n');
37 | }
38 | export function genPreviewAppEntryScript(
39 | _rootDir: string,
40 | resource: {
41 | type: ComponentResourceType;
42 | fileName: string;
43 | index?: number | undefined;
44 | },
45 | descriptor: ExtendedSFCDescriptor
46 | ): string {
47 | const instance = resourceToID({
48 | type: ComponentResourceType.COMPONENT,
49 | fileName: resource.fileName,
50 | index: resource.index,
51 | });
52 | const setup = ResourceType.USER_SETUP;
53 | const block = resource.index != null ? descriptor.previews[resource.index] : null;
54 |
55 | return [
56 | `import { createApp, x } from '${setup}'`,
57 | `import { installFetchInterceptor } from '@vuedx/preview-provider'`,
58 | `import App from '${instance}'`,
59 | ``,
60 | `installFetchInterceptor()`,
61 | ``,
62 | `const app = createApp(App, ${JSON.stringify(block?.attrs ?? {})})`,
63 | `app.provide('preview:UserProviders', x)`,
64 | `app.mount('#app')`,
65 | ``,
66 | ].join('\n');
67 | }
68 | export function genVSCodeKeyboardEventSupport(): string {
69 | return `
70 | const events = ['keydown', 'keyup'];
71 | if (window.parent !== window.top) {
72 | events.forEach(event => {
73 | document.addEventListener(event, event => {
74 | window.parent.postMessage({
75 | kind: 'event',
76 | payload: {
77 | type: event.type,
78 | init: {
79 | altKey: event.altKey,
80 | code: event.code,
81 | ctrlKey: event.ctrlKey,
82 | isComposing: event.isComposing,
83 | key: event.key,
84 | location: event.location,
85 | metaKey: event.metaKey,
86 | repeat: event.repeat,
87 | shiftKey: event.shiftKey
88 | }
89 | }
90 | }, '*')
91 | })
92 | })
93 | window.addEventListener('message', event => {
94 | if (event.source === window.self) return
95 | console.log('Message', event)
96 | const payload = event.data
97 |
98 | if (typeof payload === 'object' && payload.source === 'vscode') {
99 | switch (payload.command) {
100 | case 'setTheme': {
101 | console.log(payload)
102 | document.body.setAttribute('color-scheme', payload.args.colorScheme)
103 | }
104 | break
105 | }
106 | }
107 |
108 | // Forward to Nested iframes
109 | window.postMessage(event.data, '*')
110 | }, false)
111 | }
112 | `;
113 | }
114 |
115 | export function genEntryHTML(shellBasePath: string): string {
116 | const components = ResourceType.LIST_COMPONENTS;
117 |
118 | const html = FS.readFileSync(Path.resolve(shellBasePath, 'index.html'), 'utf-8').replace(
119 | '