@stencil/react-output-target
Version:
React output target for @stencil/core components.
1 lines • 16.9 kB
Source Map (JSON)
{"version":3,"file":"wizard.cjs","sources":["../src/wizard.ts"],"sourcesContent":["import type { StencilWizardPlugin, WizardContext } from '@stencil/cli';\nimport { Project, SyntaxKind } from 'ts-morph';\nimport { access, mkdir, writeFile } from 'node:fs/promises';\nimport { join, relative, dirname } from 'node:path';\n\n// ---------------------------------------------------------------------------\n// ts-morph helpers\n// ---------------------------------------------------------------------------\n\nfunction amendStencilConfig(configPath: string, targetCode: string, enableSsr: boolean): boolean {\n const project = new Project({ skipAddingFilesFromTsConfig: true });\n const src = project.addSourceFileAtPath(configPath);\n\n if (!src.getImportDeclaration('@stencil/react-output-target')) {\n src.addImportDeclaration({ moduleSpecifier: '@stencil/react-output-target', namedImports: ['reactOutputTarget'] });\n }\n\n const existingProp = src\n .getDescendantsOfKind(SyntaxKind.PropertyAssignment)\n .find((p) => p.getName() === 'outputTargets');\n\n if (!existingProp) {\n const configObj =\n src.getVariableDeclaration('config')?.getInitializerIfKind(SyntaxKind.ObjectLiteralExpression) ??\n src\n .getDescendantsOfKind(SyntaxKind.ObjectLiteralExpression)\n .find((obj) => obj.getProperty('namespace') !== undefined);\n if (!configObj) throw new Error('Could not find Stencil config object in stencil.config.ts');\n configObj.addPropertyAssignment({ name: 'outputTargets', initializer: '[]' });\n }\n\n const prop = src.getDescendantsOfKind(SyntaxKind.PropertyAssignment).find((p) => p.getName() === 'outputTargets')!;\n const arr = prop.getInitializerIfKind(SyntaxKind.ArrayLiteralExpression);\n if (!arr) throw new Error('outputTargets is not an array literal in stencil.config.ts');\n\n const existing = arr.getText();\n const alreadyAdded = existing.includes('reactOutputTarget(');\n\n // Filter out any existing reactOutputTarget call so it can be replaced cleanly\n const elements = arr\n .getElements()\n .map((e) => e.getText().trim())\n .filter((e) => !e.includes('reactOutputTarget('));\n\n const hasStandalone =\n existing.includes(\"type: 'standalone'\") ||\n existing.includes('type: \"standalone\"') ||\n existing.includes(\"type: 'dist-custom-elements'\") ||\n existing.includes('type: \"dist-custom-elements\"');\n if (!hasStandalone) elements.push(\"{ type: 'standalone' }\");\n\n if (enableSsr) {\n const hasSSR =\n existing.includes(\"type: 'ssr'\") ||\n existing.includes('type: \"ssr\"') ||\n existing.includes(\"type: 'dist-hydrate-script'\") ||\n existing.includes('type: \"dist-hydrate-script\"');\n if (!hasSSR) elements.push(\"{ type: 'ssr' }\");\n }\n\n elements.push(targetCode);\n\n // Replace the entire initializer as an explicitly multiline array so\n // formatText() sees a fresh structure and formats all elements consistently.\n prop.setInitializer(`[\\n${elements.map((e) => ` ${e}`).join(',\\n')},\\n]`);\n src.formatText();\n src.saveSync();\n\n return !alreadyAdded;\n}\n\n// ---------------------------------------------------------------------------\n// Wrapper package scaffolding\n// ---------------------------------------------------------------------------\n\nasync function pathExists(p: string): Promise<boolean> {\n try {\n await access(p);\n return true;\n } catch {\n return false;\n }\n}\n\nasync function scaffoldWrapperPackage(\n wrapperDir: string,\n packageName: string,\n corePackageName: string,\n corePkgVersion: string\n): Promise<void> {\n await mkdir(join(wrapperDir, 'src'), { recursive: true });\n\n const pkgJson = {\n name: packageName,\n version: '0.0.1',\n type: 'module',\n main: './dist/index.js',\n types: './dist/index.d.ts',\n exports: { '.': { types: './dist/index.d.ts', import: './dist/index.js' } },\n scripts: { build: 'tsc', dev: 'tsc --watch' },\n peerDependencies: {\n react: '^18 || ^19',\n 'react-dom': '^18 || ^19',\n },\n dependencies: { [corePackageName]: corePkgVersion },\n devDependencies: { typescript: '>=4 <7' },\n };\n\n const tsConfig = {\n compilerOptions: {\n target: 'ES2022',\n module: 'esnext',\n moduleResolution: 'bundler',\n declaration: true,\n rootDir: 'src',\n outDir: 'dist',\n strict: true,\n skipLibCheck: true,\n },\n include: ['src/**/*.ts', 'src/**/*.tsx'],\n };\n\n // components.ts lives at src/components.ts — generated by stencil build\n const indexTs =\n `// Re-exports React wrappers generated by @stencil/react-output-target.\\n` +\n `// src/components.ts is populated by \\`stencil build\\` — do not edit manually.\\n` +\n `export * from './components.js';\\n`;\n\n await Promise.all([\n writeFile(join(wrapperDir, '.gitignore'), 'dist/\\nnode_modules/\\n', 'utf8'),\n writeFile(join(wrapperDir, 'package.json'), JSON.stringify(pkgJson, null, 2) + '\\n', 'utf8'),\n writeFile(join(wrapperDir, 'tsconfig.json'), JSON.stringify(tsConfig, null, 2) + '\\n', 'utf8'),\n writeFile(join(wrapperDir, 'src', 'index.ts'), indexTs, 'utf8'),\n ]);\n}\n\n// ---------------------------------------------------------------------------\n// Wizard\n// ---------------------------------------------------------------------------\n\nexport const wizard = {\n init: {\n id: '@stencil/react-output-target',\n displayName: 'React',\n description: 'Type-safe React wrappers for your Stencil components',\n\n async run({ config, workspaceRoot, prompts, nypm }: WizardContext): Promise<void> {\n const { intro, outro, text, confirm, spinner, isCancel, cancel, log } = prompts;\n\n intro('React output target');\n\n const stencilConfigPath = join(config.rootDir, 'stencil.config.ts');\n\n // Guard: already configured?\n const guardProject = new Project({ skipAddingFilesFromTsConfig: true });\n const alreadyConfigured = guardProject\n .addSourceFileAtPath(stencilConfigPath)\n .getDescendantsOfKind(SyntaxKind.PropertyAssignment)\n .find((p) => p.getName() === 'outputTargets')\n ?.getInitializerIfKind(SyntaxKind.ArrayLiteralExpression)\n ?.getText()\n .includes('reactOutputTarget(');\n\n if (alreadyConfigured) {\n const redo = await confirm({\n message: 'reactOutputTarget is already in stencil.config.ts — reconfigure?',\n initialValue: false,\n });\n if (isCancel(redo) || !redo) {\n cancel('Skipping React setup.');\n return;\n }\n }\n\n // Where should the wrapper package live?\n let wrapperDir: string;\n let wrapperPackageName = `${config.fsNamespace}-react`;\n\n if (workspaceRoot) {\n const rawName = await text({\n message: 'Wrapper package name?',\n placeholder: wrapperPackageName,\n defaultValue: wrapperPackageName,\n });\n if (isCancel(rawName)) {\n cancel('Setup cancelled.');\n return;\n }\n wrapperPackageName = rawName as string;\n wrapperDir = join(dirname(config.rootDir), wrapperPackageName);\n } else {\n const defaultRel = `../${config.fsNamespace}-react`;\n const rawDir = await text({\n message: 'Wrapper package directory? (relative to stencil.config.ts)',\n placeholder: defaultRel,\n defaultValue: defaultRel,\n });\n if (isCancel(rawDir)) {\n cancel('Setup cancelled.');\n return;\n }\n wrapperDir = join(config.rootDir, rawDir as string);\n }\n\n // Derive the stencil.config-relative outDir from the wrapper location\n const outDir = relative(config.rootDir, join(wrapperDir, 'src')).replace(/\\\\/g, '/');\n\n // SSR\n const enableSsr = await confirm({ message: 'Enable server-side rendering (SSR)?', initialValue: false });\n if (isCancel(enableSsr)) {\n cancel('Setup cancelled.');\n return;\n }\n\n // When not in a known monorepo, ask for the npm package name separately if SSR needs it\n if (enableSsr && !workspaceRoot) {\n const rawPkgName = await text({\n message: 'React wrapper npm package name? (used as clientModule in the SSR config)',\n placeholder: wrapperPackageName,\n defaultValue: wrapperPackageName,\n });\n if (isCancel(rawPkgName)) {\n cancel('Setup cancelled.');\n return;\n }\n wrapperPackageName = rawPkgName as string;\n }\n\n // Scaffold wrapper package if the directory doesn't exist yet\n const shouldScaffold = !(await pathExists(wrapperDir));\n if (shouldScaffold) {\n const s = spinner();\n s.start(`Scaffolding wrapper package at ${relative(workspaceRoot ?? config.rootDir, wrapperDir)}`);\n try {\n const corePkgVersion = workspaceRoot\n ? 'workspace:*'\n : `file:${relative(wrapperDir, config.rootDir).replace(/\\\\/g, '/')}`;\n await scaffoldWrapperPackage(wrapperDir, wrapperPackageName, config.fsNamespace, corePkgVersion);\n s.stop('Wrapper package scaffolded');\n } catch (e) {\n s.stop('Scaffolding failed — continuing');\n log.warn(`Could not scaffold wrapper package: ${e}`);\n }\n }\n\n // Build the output target code\n const targetCode = enableSsr\n ? `reactOutputTarget({\\n outDir: '${outDir}',\\n hydrateModule: '${config.fsNamespace}/dist/ssr',\\n clientModule: '${wrapperPackageName}',\\n })`\n : `reactOutputTarget({ outDir: '${outDir}' })`;\n\n // Amend stencil.config.ts in one pass so formatText() sees the final array\n try {\n const added = amendStencilConfig(stencilConfigPath, targetCode, enableSsr as boolean);\n log.success(added ? 'stencil.config.ts updated' : 'reactOutputTarget already present — no changes made');\n } catch (e) {\n log.warn(\n `Could not automatically update stencil.config.ts (${e}). Add manually:\\n\\n` +\n `import { reactOutputTarget } from '@stencil/react-output-target';\\n` +\n `// in outputTargets:\\n${targetCode}` +\n (enableSsr ? `\\n{ type: 'ssr' }` : '')\n );\n }\n\n // Install @stencil/react-output-target as a devDep of the core package\n log.info('Installing @stencil/react-output-target...');\n await nypm.addDependency(['@stencil/react-output-target'], { cwd: config.rootDir, dev: true });\n log.success('Installed @stencil/react-output-target');\n\n // Install React peer deps in the wrapper package\n if (shouldScaffold && (await pathExists(wrapperDir))) {\n log.info('Installing React peer dependencies in wrapper package...');\n await nypm.addDependency(['@stencil/react-output-target'], { cwd: wrapperDir, dev: true });\n await nypm.addDependency(['react', 'react-dom', '@types/react', '@types/react-dom', 'typescript'], {\n cwd: wrapperDir,\n dev: true,\n });\n log.success('React peer dependencies installed');\n }\n\n outro('React output target configured');\n },\n },\n} satisfies StencilWizardPlugin;\n"],"names":["amendStencilConfig","configPath","targetCode","enableSsr","src","Project","SyntaxKind","p","configObj","_a","obj","prop","arr","existing","alreadyAdded","elements","e","pathExists","access","scaffoldWrapperPackage","wrapperDir","packageName","corePackageName","corePkgVersion","mkdir","join","pkgJson","tsConfig","writeFile","wizard","config","workspaceRoot","prompts","nypm","intro","outro","text","confirm","spinner","isCancel","cancel","log","stencilConfigPath","_b","redo","wrapperPackageName","rawName","dirname","defaultRel","rawDir","outDir","relative","rawPkgName","shouldScaffold","s","added"],"mappings":"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"}