vite-plugin-ember 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,43 @@
1
+ export declare const demoRegistry: Map<string, string>;
2
+ export interface VitePluginEmberOptions {
3
+ /**
4
+ * Path to the template compiler module.
5
+ * Default: 'ember-source/dist/ember-template-compiler'
6
+ * The standalone "ember-template-compiler.js" bundle is used because the
7
+ * ESM '@ember/template-compiler' has runtime @embroider/macros calls.
8
+ */
9
+ compilerPath?: string;
10
+ /**
11
+ * Custom import resolution map. Keys are bare specifiers used in component
12
+ * code, values are the resolved paths or package names.
13
+ *
14
+ * @example
15
+ * ```ts
16
+ * vitePluginEmber({
17
+ * resolve: {
18
+ * 'my-helpers': './src/helpers/index.js',
19
+ * 'tracked-built-ins': 'tracked-built-ins',
20
+ * }
21
+ * })
22
+ * ```
23
+ */
24
+ resolve?: Record<string, string>;
25
+ /**
26
+ * Additional Babel plugins to run during .gjs/.gts transformation.
27
+ * These are appended after the built-in template compilation and
28
+ * decorator transform plugins.
29
+ *
30
+ * @example
31
+ * ```ts
32
+ * vitePluginEmber({
33
+ * babelPlugins: [
34
+ * 'ember-concurrency/async-arrow-task-transform',
35
+ * ]
36
+ * })
37
+ * ```
38
+ */
39
+ babelPlugins?: any[];
40
+ }
41
+ export default function vitePluginEmber(options?: VitePluginEmberOptions): any;
42
+ export { emberFence } from './vitepress/ember-fence.js';
43
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAQA,eAAO,MAAM,YAAY,qBAA4B,CAAC;AAgCtD,MAAM,WAAW,sBAAsB;IACrC;;;;;OAKG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;;;;;;;;;;;;OAaG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEjC;;;;;;;;;;;;;OAaG;IACH,YAAY,CAAC,EAAE,GAAG,EAAE,CAAC;CACtB;AAED,MAAM,CAAC,OAAO,UAAU,eAAe,CACrC,OAAO,GAAE,sBAA2B,GAEnC,GAAG,CAkPL;AAGD,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,253 @@
1
+ import { transformAsync } from '@babel/core';
2
+ import templateCompilation from 'babel-plugin-ember-template-compilation';
3
+ import { Preprocessor } from 'content-tag';
4
+ import { createRequire } from 'node:module';
5
+ import { existsSync } from 'node:fs';
6
+ // ── Shared demo registry (populated by ember-fence, read by load hook) ──
7
+ export const demoRegistry = new Map();
8
+ // ── Virtual-module helpers (for inline markdown demos) ──────────────────
9
+ const PUBLIC_PREFIX = 'virtual:ember-demo-';
10
+ /**
11
+ * Virtual module ID for the @embroider/macros runtime shim.
12
+ * ember-source's ESM modules import from @embroider/macros and call compile-time
13
+ * functions like isDevelopingApp(). Outside of the Ember build pipeline these need
14
+ * runtime implementations instead of throwing.
15
+ */
16
+ const EMBROIDER_MACROS_ID = '\0embroider-macros-shim';
17
+ const EMBROIDER_MACROS_CODE = `
18
+ export function isDevelopingApp() { return true; }
19
+ export function isTesting() { return false; }
20
+ export function macroCondition(condition) { return condition; }
21
+ export function dependencySatisfies() { return true; }
22
+ export function getOwnConfig() { return {}; }
23
+ export function getConfig() { return {}; }
24
+ export function importSync(specifier) {
25
+ throw new Error('[embroider-macros shim] importSync is not supported at runtime: ' + specifier);
26
+ }
27
+ export function getGlobalConfig() { return { isDevelopingApp: true, isTesting: false }; }
28
+ `;
29
+ /**
30
+ * Bare-specifier prefixes that should be resolved into ember-source's
31
+ * dist/packages tree. E.g. `@ember/renderer` →
32
+ * `<ember-source>/dist/packages/@ember/renderer/index.js`
33
+ */
34
+ const EMBER_PACKAGE_PREFIXES = ['@ember/', '@glimmer/'];
35
+ export default function vitePluginEmber(options = {}) {
36
+ const preprocessor = new Preprocessor();
37
+ // Locate ember-source's dist/packages directory on disk
38
+ let emberSourcePackagesDir;
39
+ // The loaded template compiler object (passed directly to babel plugin)
40
+ let compilerObj;
41
+ // createRequire anchored to the project root — used to resolve node_modules
42
+ let rootRequire;
43
+ // ── Resolution caches (avoid repeated fs lookups & require.resolve) ──
44
+ const resolveCache = new Map();
45
+ let decoratorRuntimePath;
46
+ // ── Pre-built babel configs (allocated once, cloned per-transform) ──
47
+ let babelConfigGJS;
48
+ let babelConfigGTS;
49
+ function buildBabelConfigs() {
50
+ const baseParserPlugins = [
51
+ 'classProperties',
52
+ 'classPrivateProperties',
53
+ 'classPrivateMethods',
54
+ ];
55
+ const basePlugins = [
56
+ [templateCompilation, { compiler: compilerObj }],
57
+ [
58
+ 'module:decorator-transforms',
59
+ { runtime: { import: 'decorator-transforms/runtime' } },
60
+ ],
61
+ ...(options.babelPlugins ?? []),
62
+ ];
63
+ babelConfigGJS = {
64
+ plugins: basePlugins,
65
+ parserPlugins: baseParserPlugins,
66
+ };
67
+ babelConfigGTS = {
68
+ plugins: [
69
+ ...basePlugins,
70
+ [
71
+ '@babel/plugin-transform-typescript',
72
+ {
73
+ allExtensions: true,
74
+ onlyRemoveTypeImports: true,
75
+ allowDeclareFields: true,
76
+ },
77
+ ],
78
+ ],
79
+ parserPlugins: [...baseParserPlugins, 'typescript'],
80
+ };
81
+ }
82
+ /**
83
+ * Look up a bare @ember/* or @glimmer/* specifier.
84
+ * Results are cached to avoid repeated existsSync / require.resolve calls.
85
+ */
86
+ function resolveEmberPackage(id) {
87
+ if (resolveCache.has(id))
88
+ return resolveCache.get(id);
89
+ let resolved = null;
90
+ if (emberSourcePackagesDir) {
91
+ const dirPath = `${emberSourcePackagesDir}/${id}/index.js`;
92
+ if (existsSync(dirPath)) {
93
+ resolved = dirPath;
94
+ }
95
+ else {
96
+ const filePath = `${emberSourcePackagesDir}/${id}.js`;
97
+ if (existsSync(filePath)) {
98
+ resolved = filePath;
99
+ }
100
+ }
101
+ }
102
+ // Fallback: resolve from node_modules (e.g. @glimmer/component@2)
103
+ if (!resolved && rootRequire) {
104
+ try {
105
+ resolved = rootRequire.resolve(id);
106
+ }
107
+ catch {
108
+ // not installed
109
+ }
110
+ }
111
+ resolveCache.set(id, resolved);
112
+ return resolved;
113
+ }
114
+ return {
115
+ name: 'vite-plugin-ember',
116
+ enforce: 'pre',
117
+ /* ─── locate ember-source from the project root ───────────────── */
118
+ configResolved(config) {
119
+ if (!emberSourcePackagesDir) {
120
+ try {
121
+ rootRequire = createRequire(config.root + '/package.json');
122
+ const emberPkg = rootRequire.resolve('ember-source/package.json');
123
+ emberSourcePackagesDir = emberPkg.replace(/package\.json$/, 'dist/packages');
124
+ // Load the standalone template compiler (CJS bundle)
125
+ if (options.compilerPath) {
126
+ compilerObj = rootRequire(options.compilerPath);
127
+ }
128
+ else {
129
+ compilerObj = rootRequire('ember-source/dist/ember-template-compiler.js');
130
+ }
131
+ // Pre-resolve decorator-transforms runtime (CJS → ESM)
132
+ try {
133
+ const cjsPath = rootRequire.resolve('decorator-transforms/runtime');
134
+ decoratorRuntimePath = cjsPath.replace(/runtime\.cjs$/, 'runtime.js');
135
+ }
136
+ catch {
137
+ // not installed
138
+ }
139
+ // Build reusable babel config objects now that compiler is loaded
140
+ buildBabelConfigs();
141
+ }
142
+ catch {
143
+ // ember-source not found
144
+ }
145
+ }
146
+ },
147
+ /* ─── serve virtual modules before VitePress catches them ─────── */
148
+ configureServer(server) {
149
+ server.middlewares.use(async (req, res, next) => {
150
+ const url = req.url ?? '';
151
+ // Only intercept virtual ember-demo modules — file-based .gjs/.gts
152
+ // are handled natively by Vite's transform pipeline.
153
+ if (!url.includes('virtual:ember-demo-'))
154
+ return next();
155
+ try {
156
+ const idMatch = url.match(/(virtual:ember-demo-[^?&]+)/);
157
+ if (!idMatch)
158
+ return next();
159
+ const result = await server.transformRequest(idMatch[1], {
160
+ ssr: false,
161
+ });
162
+ if (!result)
163
+ return next();
164
+ res.setHeader('Content-Type', 'application/javascript');
165
+ res.setHeader('Cache-Control', 'no-cache');
166
+ res.end(result.code);
167
+ }
168
+ catch (err) {
169
+ console.error('[vite-plugin-ember] middleware error:', err);
170
+ next();
171
+ }
172
+ });
173
+ },
174
+ /* ─── resolve virtual modules + @ember/* / @glimmer/* ─────────── */
175
+ resolveId(id) {
176
+ if (id.startsWith('\0'))
177
+ return null;
178
+ // Shim @embroider/macros with runtime implementations
179
+ if (id === '@embroider/macros')
180
+ return EMBROIDER_MACROS_ID;
181
+ // Resolve decorator-transforms runtime (pre-cached ESM path)
182
+ if (id === 'decorator-transforms/runtime') {
183
+ return decoratorRuntimePath ?? null;
184
+ }
185
+ // Virtual ember-demo modules (with or without /@id/ prefix)
186
+ let stripped = id;
187
+ if (stripped.startsWith('/@id/'))
188
+ stripped = stripped.slice('/@id/'.length);
189
+ const bareStripped = stripped.split('?')[0];
190
+ if (bareStripped.startsWith(PUBLIC_PREFIX))
191
+ return bareStripped;
192
+ // Resolve bare @ember/* and @glimmer/* to ember-source dist (cached)
193
+ for (const prefix of EMBER_PACKAGE_PREFIXES) {
194
+ if (id.startsWith(prefix)) {
195
+ return resolveEmberPackage(id);
196
+ }
197
+ }
198
+ // User-provided custom resolution map
199
+ if (options.resolve?.[id]) {
200
+ return options.resolve[id];
201
+ }
202
+ return null;
203
+ },
204
+ /* ─── load virtual module source from registry ────────────────── */
205
+ load(id) {
206
+ if (id === EMBROIDER_MACROS_ID)
207
+ return EMBROIDER_MACROS_CODE;
208
+ if (!id.startsWith(PUBLIC_PREFIX))
209
+ return null;
210
+ return demoRegistry.get(id) ?? null;
211
+ },
212
+ /* ─── transform .gjs / .gts files ─────────────────────────────── */
213
+ async transform(code, id) {
214
+ const bareId = id.split('?', 1)[0];
215
+ const isGJS = bareId.endsWith('.gjs');
216
+ const isGTS = bareId.endsWith('.gts');
217
+ if (!isGJS && !isGTS)
218
+ return null;
219
+ // ── Step 1: content-tag preprocessing ──
220
+ let preprocessed;
221
+ try {
222
+ const result = preprocessor.process(code, { filename: bareId });
223
+ preprocessed =
224
+ typeof result === 'string'
225
+ ? result
226
+ : (result.code ?? result);
227
+ }
228
+ catch (err) {
229
+ this.error(`[vite-plugin-ember] content-tag failed for ${bareId}: ${err.message}`);
230
+ return null;
231
+ }
232
+ // ── Step 2: Babel (template compilation + decorators + optional TS) ──
233
+ const cfg = isGTS ? babelConfigGTS : babelConfigGJS;
234
+ const result = await transformAsync(preprocessed, {
235
+ filename: bareId,
236
+ babelrc: false,
237
+ configFile: false,
238
+ plugins: cfg.plugins,
239
+ parserOpts: {
240
+ sourceType: 'module',
241
+ plugins: cfg.parserPlugins,
242
+ },
243
+ sourceMaps: true,
244
+ });
245
+ if (!result?.code)
246
+ return null;
247
+ return { code: result.code, map: result.map };
248
+ },
249
+ };
250
+ }
251
+ // ── Re-exports for VitePress markdown integration ───────────────────────
252
+ export { emberFence } from './vitepress/ember-fence.js';
253
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type MarkdownIt from 'markdown-it';
2
+ /** Markdown-it plugin: ```gjs live → <CodePreview /> */
3
+ export declare function emberFence(md: MarkdownIt, component?: string): void;
4
+ //# sourceMappingURL=ember-fence.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ember-fence.d.ts","sourceRoot":"","sources":["../../src/vitepress/ember-fence.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,UAAU,MAAM,aAAa,CAAC;AAsB1C,wDAAwD;AACxD,wBAAgB,UAAU,CAAC,EAAE,EAAE,UAAU,EAAE,SAAS,SAAgB,QAsCnE"}
@@ -0,0 +1,57 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { demoRegistry } from '../index.js';
3
+ function makeVirtualId(code, lang) {
4
+ const hash = createHash('sha1').update(code).digest('hex').slice(0, 8);
5
+ const moduleId = `virtual:ember-demo-${hash}.${lang}`;
6
+ // Store source in the shared registry (read by the Vite plugin's load hook)
7
+ demoRegistry.set(moduleId, code);
8
+ return moduleId;
9
+ }
10
+ /**
11
+ * Replace `<Component src="...">` with `:loader="() => import('...')"` so
12
+ * Vite can statically analyse and bundle the import in production (SSG) builds.
13
+ */
14
+ function transformSrcToLoader(content, component) {
15
+ const re = new RegExp(`<${component}\\s+src="([^"]+)"`, 'g');
16
+ return content.replace(re, (_, src) => `<${component} :loader="() => import('${src}')"`);
17
+ }
18
+ /** Markdown-it plugin: ```gjs live → <CodePreview /> */
19
+ export function emberFence(md, component = 'CodePreview') {
20
+ const originalFence = md.renderer.rules.fence;
21
+ md.renderer.rules.fence = (tokens, idx, options, env, self) => {
22
+ const token = tokens[idx];
23
+ const info = (token.info || '').trim();
24
+ const [lang, ...flags] = info.split(/\s+/);
25
+ if ((lang === 'gjs' || lang === 'gts') && flags.includes('live')) {
26
+ const virtualId = makeVirtualId(token.content, lang);
27
+ if (flags.includes('preview')) {
28
+ // Show rendered component + syntax-highlighted code block.
29
+ // Strip 'live' and 'preview' flags so the original fence renders
30
+ // a normal Shiki-highlighted block instead of triggering us again.
31
+ const savedInfo = token.info;
32
+ token.info = lang;
33
+ const highlighted = originalFence(tokens, idx, options, env, self);
34
+ token.info = savedInfo;
35
+ return `<${component} :loader="() => import('${virtualId}')">${highlighted}</${component}>`;
36
+ }
37
+ return `<${component} :loader="() => import('${virtualId}')" />`;
38
+ }
39
+ return originalFence(tokens, idx, options, env, self);
40
+ };
41
+ // Transform file-based <CodePreview src="..."> tags so Vite can bundle them
42
+ md.core.ruler.push('ember-loader-transform', (state) => {
43
+ for (const token of state.tokens) {
44
+ if (token.type === 'html_block' || token.type === 'html_inline') {
45
+ token.content = transformSrcToLoader(token.content, component);
46
+ }
47
+ if (token.children) {
48
+ for (const child of token.children) {
49
+ if (child.type === 'html_inline') {
50
+ child.content = transformSrcToLoader(child.content, component);
51
+ }
52
+ }
53
+ }
54
+ }
55
+ });
56
+ }
57
+ //# sourceMappingURL=ember-fence.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ember-fence.js","sourceRoot":"","sources":["../../src/vitepress/ember-fence.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAG3C,SAAS,aAAa,CAAC,IAAY,EAAE,IAAmB;IACtD,MAAM,IAAI,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACvE,MAAM,QAAQ,GAAG,sBAAsB,IAAI,IAAI,IAAI,EAAE,CAAC;IACtD,4EAA4E;IAC5E,YAAY,CAAC,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IACjC,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;;GAGG;AACH,SAAS,oBAAoB,CAAC,OAAe,EAAE,SAAiB;IAC9D,MAAM,EAAE,GAAG,IAAI,MAAM,CAAC,IAAI,SAAS,mBAAmB,EAAE,GAAG,CAAC,CAAC;IAC7D,OAAO,OAAO,CAAC,OAAO,CACpB,EAAE,EACF,CAAC,CAAC,EAAE,GAAW,EAAE,EAAE,CAAC,IAAI,SAAS,2BAA2B,GAAG,KAAK,CACrE,CAAC;AACJ,CAAC;AAED,wDAAwD;AACxD,MAAM,UAAU,UAAU,CAAC,EAAc,EAAE,SAAS,GAAG,aAAa;IAClE,MAAM,aAAa,GAAG,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAM,CAAC;IAC/C,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;QAC5D,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;QAC1B,MAAM,IAAI,GAAG,CAAC,KAAK,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QACvC,MAAM,CAAC,IAAI,EAAE,GAAG,KAAK,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAC3C,IAAI,CAAC,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;YACjE,MAAM,SAAS,GAAG,aAAa,CAAC,KAAK,CAAC,OAAO,EAAE,IAAqB,CAAC,CAAC;YACtE,IAAI,KAAK,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;gBAC9B,2DAA2D;gBAC3D,iEAAiE;gBACjE,mEAAmE;gBACnE,MAAM,SAAS,GAAG,KAAK,CAAC,IAAI,CAAC;gBAC7B,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC;gBAClB,MAAM,WAAW,GAAG,aAAa,CAAC,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;gBACnE,KAAK,CAAC,IAAI,GAAG,SAAS,CAAC;gBACvB,OAAO,IAAI,SAAS,2BAA2B,SAAS,OAAO,WAAW,KAAK,SAAS,GAAG,CAAC;YAC9F,CAAC;YACD,OAAO,IAAI,SAAS,2BAA2B,SAAS,QAAQ,CAAC;QACnE,CAAC;QACD,OAAO,aAAa,CAAC,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;IACxD,CAAC,CAAC;IAEF,4EAA4E;IAC5E,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,wBAAwB,EAAE,CAAC,KAAK,EAAE,EAAE;QACrD,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;YACjC,IAAI,KAAK,CAAC,IAAI,KAAK,YAAY,IAAI,KAAK,CAAC,IAAI,KAAK,aAAa,EAAE,CAAC;gBAChE,KAAK,CAAC,OAAO,GAAG,oBAAoB,CAAC,KAAK,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;YACjE,CAAC;YACD,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;gBACnB,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;oBACnC,IAAI,KAAK,CAAC,IAAI,KAAK,aAAa,EAAE,CAAC;wBACjC,KAAK,CAAC,OAAO,GAAG,oBAAoB,CAAC,KAAK,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;oBACjE,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC"}
package/package.json ADDED
@@ -0,0 +1,86 @@
1
+ {
2
+ "name": "vite-plugin-ember",
3
+ "version": "0.0.0",
4
+ "description": "Vite plugin for rendering live Ember components in VitePress documentation",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "author": "Alexey Kulakov",
8
+ "homepage": "https://github.com/aklkv/vite-plugin-ember#readme",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "https://github.com/aklkv/vite-plugin-ember.git",
12
+ "directory": "vite-plugin-ember"
13
+ },
14
+ "bugs": {
15
+ "url": "https://github.com/aklkv/vite-plugin-ember/issues"
16
+ },
17
+ "keywords": [
18
+ "vite",
19
+ "vite-plugin",
20
+ "ember",
21
+ "glimmer",
22
+ "vitepress",
23
+ "gjs",
24
+ "gts"
25
+ ],
26
+ "sideEffects": false,
27
+ "main": "dist/index.js",
28
+ "types": "dist/index.d.ts",
29
+ "exports": {
30
+ ".": {
31
+ "types": "./dist/index.d.ts",
32
+ "default": "./dist/index.js"
33
+ },
34
+ "./components/code-preview.vue": "./src/vitepress/code-preview.vue",
35
+ "./components/CodePreview": "./src/vitepress/code-preview.vue",
36
+ "./*": {
37
+ "types": "./dist/*.d.ts",
38
+ "default": "./dist/*.js"
39
+ }
40
+ },
41
+ "files": [
42
+ "dist",
43
+ "src/vitepress/code-preview.vue"
44
+ ],
45
+ "scripts": {
46
+ "build": "tsc -p tsconfig.json",
47
+ "lint": "concurrently 'npm:lint:*(!fix)' --names 'lint:'",
48
+ "lint:fix": "concurrently 'npm:lint:*:fix' --names 'fix:'",
49
+ "lint:js": "eslint .",
50
+ "lint:js:fix": "eslint . --fix",
51
+ "lint:format": "prettier . --cache --check",
52
+ "lint:format:fix": "prettier . --cache --write",
53
+ "lint:types": "tsc --noEmit",
54
+ "format": "prettier . --cache --write",
55
+ "format:check": "prettier . --cache --check"
56
+ },
57
+ "dependencies": {
58
+ "@babel/core": "^7.29.0",
59
+ "@babel/plugin-transform-typescript": "^7.28.6",
60
+ "babel-plugin-ember-template-compilation": "^4.0.0",
61
+ "content-tag": "^4.1.0",
62
+ "decorator-transforms": "^2.3.1"
63
+ },
64
+ "devDependencies": {
65
+ "@eslint/js": "^9.39.2",
66
+ "@types/babel__core": "^7.20.5",
67
+ "@types/markdown-it": "^14.1.2",
68
+ "@types/node": "^25.2.3",
69
+ "concurrently": "^9.2.1",
70
+ "ember-source": "6.10.1",
71
+ "eslint": "^9.39.2",
72
+ "eslint-config-prettier": "^10.1.8",
73
+ "globals": "^17.3.0",
74
+ "markdown-it": "^14.1.1",
75
+ "typescript": "^5.9.3",
76
+ "typescript-eslint": "^8.55.0",
77
+ "vite": "^7.3.1",
78
+ "vitepress": "^1.6.4",
79
+ "vue": "^3.5.28"
80
+ },
81
+ "peerDependencies": {
82
+ "vite": ">=5",
83
+ "vitepress": ">=1",
84
+ "vue": ">=3"
85
+ }
86
+ }
@@ -0,0 +1,70 @@
1
+ <script setup lang="ts">
2
+ import { onMounted, onBeforeUnmount, ref } from 'vue';
3
+ import { inBrowser } from 'vitepress';
4
+
5
+ const props = defineProps<{
6
+ src?: string;
7
+ loader?: () => Promise<any>;
8
+ }>();
9
+ const mountEl = ref<HTMLDivElement | null>(null);
10
+ const error = ref<string | null>(null);
11
+ let cleanup: undefined | { destroy?: () => void };
12
+
13
+ // Cache the renderer import — shared across all CodePreview instances
14
+ let rendererPromise: Promise<{ renderComponent: Function }> | undefined;
15
+
16
+ function getRenderer() {
17
+ rendererPromise ??= import('@ember/renderer') as any;
18
+ return rendererPromise!;
19
+ }
20
+
21
+ onMounted(async () => {
22
+ if (!inBrowser || !mountEl.value) return;
23
+
24
+ try {
25
+ const [mod, { renderComponent }] = await Promise.all([
26
+ props.loader ? props.loader() : import(/* @vite-ignore */ props.src!),
27
+ getRenderer(),
28
+ ]);
29
+
30
+ const component = mod?.default ?? mod;
31
+ cleanup = renderComponent(component, { into: mountEl.value });
32
+ } catch (err) {
33
+ console.error('[CodePreview] Failed to render:', err);
34
+ error.value = String(err);
35
+ }
36
+ });
37
+
38
+ onBeforeUnmount(() => {
39
+ cleanup?.destroy?.();
40
+ cleanup = undefined;
41
+ });
42
+ </script>
43
+
44
+ <template>
45
+ <div class="ember-playground">
46
+ <div v-if="error" class="ember-playground__error">{{ error }}</div>
47
+ <div ref="mountEl"></div>
48
+ <slot />
49
+ </div>
50
+ </template>
51
+
52
+ <style scoped>
53
+ .ember-playground {
54
+ padding: 12px;
55
+ border: 1px solid var(--vp-c-divider);
56
+ border-radius: 10px;
57
+ }
58
+
59
+ .ember-playground__error {
60
+ padding: 8px 12px;
61
+ margin-bottom: 8px;
62
+ border-radius: 6px;
63
+ background: var(--vp-c-danger-soft);
64
+ color: var(--vp-c-danger-1);
65
+ font-size: 13px;
66
+ font-family: var(--vp-font-family-mono);
67
+ white-space: pre-wrap;
68
+ word-break: break-word;
69
+ }
70
+ </style>