@knapsack/renderer-react 4.70.0--canary.3797.b249674.0 → 4.70.0--canary.4821.56b0218.0

Sign up to get free protection for your applications and to get access to all the features.
package/dist/index.mjs CHANGED
@@ -1,46 +1,1301 @@
1
- import Ie from 'sleep-promise';
2
- import { RendererWebpackBase } from '@knapsack/renderer-webpack-base';
3
- import { makeShortId, pascalCase, isFirstLetterCapital, removeWrappingDoubleQuotes } from '@knapsack/utils';
4
- import { log, knapsackEvents } from '@knapsack/app';
5
- import { babelCodeForBrowser, createSlotOptionsHtmlTags } from '@knapsack/app/renderers';
6
- import { rendererIds, isNumberProp, isOptionsProp } from '@knapsack/types';
7
- import { findUpPkgJson, formatCode, getJsExportNames, exists, readFile, findUp, resolvePath, getModulePkgJson, copy } from '@knapsack/file-utils';
8
- import h, { join, parse, relative } from 'path';
9
- import { createCreator, tasks } from '@knapsack/creator-utils';
10
- import { readKsPatternConfigs, writeDemo, writeKsPatternConfig, readKsNavConfig, addKsNavItems } from '@knapsack/ks-file-utils';
1
+ var __defProp = Object.defineProperty;
2
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
3
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
4
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
5
+ }) : x)(function(x) {
6
+ if (typeof require !== "undefined") return require.apply(this, arguments);
7
+ throw Error('Dynamic require of "' + x + '" is not supported');
8
+ });
11
9
 
12
- var N=Object.defineProperty;var c=(r,e)=>N(r,"name",{value:e,configurable:!0}),K=(r=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(r,{get:(e,t)=>(typeof require<"u"?require:e)[t]}):r)(function(r){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+r+'" is not supported')});function ee({templateName:r,attributes:e,children:t}){return `
13
- <${r}
14
- ${e}
15
- ${t?`>
16
- ${t}
17
- </${r}>`:"/>"}
18
- `}c(ee,"renderUsageTemplate");var O="DemoApp";function te({imports:r="",children:e}){return `${r}
10
+ // ../../../../node_modules/.pnpm/tsup@8.2.4_@microsoft+api-extractor@7.43.8_@types+node@20.16.6__@swc+core@1.3.57_@swc+helpers_iqa4vqpgdlzwuubzthdmdz7nfe/node_modules/tsup/assets/esm_shims.js
11
+ import { fileURLToPath } from "url";
12
+ import path from "path";
13
+ var getFilename = /* @__PURE__ */ __name(() => fileURLToPath(import.meta.url), "getFilename");
14
+ var getDirname = /* @__PURE__ */ __name(() => path.dirname(getFilename()), "getDirname");
15
+ var __dirname = /* @__PURE__ */ getDirname();
19
16
 
20
- function ${O}() {
17
+ // src/renderer-react.ts
18
+ import sleep from "sleep-promise";
19
+ import {
20
+ RendererWebpackBase,
21
+ convertImportMapToNeededImportsByPath
22
+ } from "@knapsack/renderer-webpack-base";
23
+ import { pascalCase, isFirstLetterCapital } from "@knapsack/utils";
24
+ import { knapsackEvents, log as log2 } from "@knapsack/app";
25
+ import {
26
+ babelCodeForBrowser,
27
+ createSlotOptionsHtmlTags
28
+ } from "@knapsack/app/renderers";
29
+ import {
30
+ rendererIds
31
+ } from "@knapsack/types";
32
+ import {
33
+ exists as fileExists,
34
+ findUpPkgJson,
35
+ getJsExportNames as getJsExportNames2,
36
+ readFile as readFile2,
37
+ formatCode as formatCode2,
38
+ assertFileExists
39
+ } from "@knapsack/file-utils";
40
+ import { join, relative, parse, isAbsolute } from "path";
41
+
42
+ // src/utils.ts
43
+ import { log } from "@knapsack/app";
44
+ import {
45
+ getModulePkgJson,
46
+ readFile,
47
+ copy,
48
+ formatCode,
49
+ resolvePath,
50
+ findUp
51
+ } from "@knapsack/file-utils";
52
+ import { removeWrappingDoubleQuotes } from "@knapsack/utils";
53
+ import path2 from "path";
54
+ import { isNumberProp, isOptionsProp } from "@knapsack/types";
55
+ function renderUsageTemplate({
56
+ templateName,
57
+ attributes,
58
+ children
59
+ }) {
60
+ return `
61
+ <${templateName}
62
+ ${attributes}
63
+ ${children ? `>
64
+ ${children}
65
+ </${templateName}>` : "/>"}
66
+ `;
67
+ }
68
+ __name(renderUsageTemplate, "renderUsageTemplate");
69
+ var demoAppName = "DemoApp";
70
+ function renderDemoAppTemplate({
71
+ imports = "",
72
+ children
73
+ }) {
74
+ return `${imports}
75
+
76
+ function ${demoAppName}() {
21
77
  return (
22
- ${e}
78
+ ${children}
23
79
  )
24
80
  }
25
- `}c(te,"renderDemoAppTemplate");async function M(r){let e=Object.keys(r.props||{}).map(n=>{let p=r.props[n];return {key:n,value:p}}),{templateName:t,children:s,extraProps:a=[]}=r;if(!t)throw new Error('Cannot getUsage of a React Component when no "templateName" is provided.');let o=e.map(({key:n,value:p})=>{switch(typeof p){case"string":return p.startsWith("(")&&p.includes("=>")?`${n}={${p}}`:`${n}="${p}"`;case"boolean":return p?`${n}`:`${n}={${p}}`;default:return `${n}={${JSON.stringify(p)}}`}}),i=[],l={};a.forEach(({key:n,value:p})=>{l[n]=l[n]??[],l[n].push(p);}),Object.entries(l).forEach(([n,p])=>{let u=p.length===1?p[0]:`<>${p.join(`
26
- `)}</>`;i.push(`${n}={${u}}`);});let m=ee({templateName:t,attributes:[...o,...i].join(" "),children:s});return r.format?formatCode({contents:m,path:"x.tsx"}).then(n=>n.trim()).catch(()=>m.trim()):m.trim()}c(M,"getUsage");async function v({children:r,imports:e,format:t}){let s=te({children:r,imports:e});return t?formatCode({contents:s,path:"x.tsx"}):s}c(v,"getDemoAppUsage");async function J({src:r}){let e=await findUp("tsconfig.json",{cwd:h.dirname(r)}),t=await import('react-docgen-typescript'),s={shouldExtractLiteralValuesFromEnum:!0,savePropValueAsString:!0,skipChildrenPropWithoutDoc:!1,customComponentTypes:["VoidFunctionComponent","VFC"]};return {info:(e?t.withCustomConfig(e,s).parse:t.withDefaultConfig(s).parse)(r)}}c(J,"getTypeScriptInfoFromFile");function re({info:r,exportName:e}){try{if(!r)return !1;let t={props:{$schema:"http://json-schema.org/draft-07/schema",type:"object",required:[],properties:{}},slots:{}},a=!e||e==="default"?r.pop():r.find(i=>i.displayName===e);if(!a)return !1;return Object.entries(a?.props||{}).forEach(([i,l])=>{let{name:m,description:n,defaultValue:p,required:u,type:f,parent:y}=l;switch(f?.name?.replace("| undefined","").replace(/<.*/g,"").trim()){case"string":t.props.properties[i]={type:"string"};break;case"number":t.props.properties[i]={type:"number"};break;case"boolean":case"bool":p&&"value"in p&&(p.value=p.value==="true"),t.props.properties[i]={type:"boolean"};break;case"enum":t.props.properties[i]={type:"string",enum:[...new Set(f.value.map(({value:d})=>removeWrappingDoubleQuotes(d?.trim())).filter(Boolean))]};break;case"ReactNode":case"React.ReactNode":case"React.ReactElement":case"ReactElement":t.slots[i]={title:i},n&&(t.slots[i]={...t.slots[i],description:n});break;case"VFC":case"FC":t.slots[i]={title:i,description:n||"A reference to a component",isTemplateReference:!0,allowOnlyOne:!0,disallowText:!0};break;default:if(f?.name?.startsWith("("))t.props.properties[i]={typeof:"function",tsType:l?.type?.name};else if(f?.name?.includes("|")){let d=f.name.split("|").map(P=>removeWrappingDoubleQuotes(P.trim())).filter(Boolean);d?.length&&(t.props.properties[i]={type:"string",enum:[...new Set(d)]});}else t.props.properties[i]={tsType:l?.type?.name};}t.props.properties[i]&&(u&&t.props.required.push(i),n&&!t.props.properties[i].description&&(t.props.properties[i].description=n),p&&"value"in p&&(isNumberProp(t.props.properties[i])?t.props.properties[i].default=parseFloat(p.value):t.props.properties[i].default=removeWrappingDoubleQuotes(p.value)));}),t}catch(t){return log.verbose("Could not infer spec from React TypeScript file",{exportName:e,error:t},"react renderer"),!1}}c(re,"extractSpecFromTypeScriptInfo");async function se({src:r}){let e=await import('react-docgen'),{builtinResolvers:t}=e,s=await readFile(r),{FindExportedDefinitionsResolver:a}=t;return {info:e.parse(s,{resolver:new a,handlers:null,filename:r})}}c(se,"getPropTypesInfoFromFile");function ae({info:r,exportName:e}){try{let s=!e||e==="default"?r.pop():r.find(o=>o.displayName===e),a={isInferred:!0,props:{$schema:"http://json-schema.org/draft-07/schema",type:"object",required:[],properties:{}},slots:{}};return Object.entries(s?.props||{}).forEach(([o,i])=>{let{required:l,description:m,defaultValue:n}=i;switch(i?.type?.name){case"string":a.props.properties[o]={type:"string"};break;case"func":a.props.properties[o]={type:"string"};break;case"bool":a.props.properties[o]={type:"boolean"};break;case"node":a.slots[o]={title:o,description:m};}a.props.properties[o]&&(l&&a.props.required.push(o),m&&!a.props.properties[o].description&&(a.props.properties[o].description=m),n&&"value"in n&&(a.props.properties[o].default=typeof n.value=="string"?removeWrappingDoubleQuotes(n.value):n.value));}),a}catch(t){return log.verbose("Could not infer spec from React PropTypes",{exportName:e,error:t},"react renderer"),!1}}c(ae,"extractSpecFromPropTypesInfo");function ne({spec:r}){return r===!1||Object.entries(r?.props?.properties||{}).forEach(([e,t])=>{isOptionsProp(t)&&(t.enum.includes(t.default)||(t.default=void 0));}),r}c(ne,"cleanUpSpec");async function oe({src:r,resolveFromDir:e}){let t=await resolvePath({path:r,resolveFromDir:e,resolveType:"types"});if(t.exists){let o=await J({src:t.absolutePath});if(o)return {type:"typescript",info:o.info}}let s=await resolvePath({path:r,resolveFromDir:e});if(!s.exists)return {type:"unknown"};let{ext:a}=h.parse(s.absolutePath);switch(a){case".jsx":return {type:"propTypes",info:(await se({src:s.absolutePath})).info};case".ts":case".tsx":return {type:"typescript",info:(await J({src:s.absolutePath})).info};default:return {type:"unknown"}}}c(oe,"getReactModuleInfoUncached");var $=new Map,V=c(()=>{log.info("Clearing React TypeScript inferSpec cache..."),$.clear();},"clearInferSpecCache");async function ie(r){let e=JSON.stringify(r);return $.has(e)||$.set(e,oe(r)),$.get(e)}c(ie,"getReactModuleInfo");async function pe({src:r,exportName:e,resolveFromDir:t}){let s=await ie({src:r,resolveFromDir:t});switch(s.type){case"typescript":return re({info:s.info,exportName:e});case"propTypes":return ae({info:s.info,exportName:e});case"unknown":default:return !1}}c(pe,"getReactSpec");async function B(r){return ne({spec:await pe(r)})}c(B,"getReactDocs");async function L(r,e){try{let{version:t}=getModulePkgJson("react"),{version:s}=getModulePkgJson("react-dom"),a=h.dirname(K.resolve("react",{paths:[process.cwd()]})),o=h.dirname(K.resolve("react-dom",{paths:[process.cwd()]}));await Promise.all([copy(h.join(a,"umd/react.development.js"),h.join(r,`react.development.${t}.js`)),copy(h.join(a,"umd/react.production.min.js"),h.join(r,`react.production.min.${t}.js`)),copy(h.join(o,"umd/react-dom.production.min.js"),h.join(r,`react-dom.production.min.${s}.js`)),copy(h.join(o,"umd/react-dom.development.js"),h.join(r,`react-dom.development.${s}.js`))]);let i=process.env.NODE_ENV==="production"?"production.min":"development";return [h.join(e,`react.${i}.${t}.js`),h.join(e,`react-dom.${i}.${s}.js`)]}catch(t){log.warn('Error trying to copy "react" and "react-dom" JS files, are they installed? We want to use your exact versions.',t,"templateRenderer:react"),process.exit(1);}}c(L,"copyReactAssets");function ye(r){return r.toUpperCase()===r}c(ye,"isCapitalLetter");function we(r){return ye(r[0])}c(we,"startsWithCapitalLetter");function Pe({importName:r,id:e,title:t=r,pkgPath:s,initialDemoId:a}){return {id:e,title:t,description:"",statuses:{main:"ready"},templates:[{id:"react",title:"react",path:s,alias:r,templateLanguageId:"react",spec:{isInferred:!0},demoIds:[a],blockIds:[]}],tabs:[{type:"template",id:"react"}],subPages:[]}}c(Pe,"createPatternData");var G=createCreator({id:"react-patterns",title:"React Ks Patterns",description:"Adds React templates as Knapsack Patterns",getQuestions:c(async()=>({pkgPath:{type:"text",title:"Package path"},importPrefix:{type:"text",title:"Import Prefix to Remove"}}),"getQuestions"),getTasks:c(async({answers:{pkgPath:r,importPrefix:e=""},config:t})=>{let s=t.dest,o=(await readKsPatternConfigs({dataDir:s})).reduce((l,m)=>{let n=m.templates?.filter(p=>p.templateLanguageId==="react")??[];return l.push(...n.map(p=>p.alias)),l},[]),{exports:i}=await getJsExportNames({path:r});return [{title:"Pick Imports to add",task:c((l,m)=>tasks.runSubCreator({task:m,config:t,creator:createCreator({id:"react-pattern-import-names",getQuestions:c(async()=>({importNames:{type:"choices",choices:i.filter(n=>we(n)&&!o.includes(n)).map(n=>({value:n}))}}),"getQuestions"),getTasks:c(async({answers:{importNames:n}})=>{let p=n.map(u=>({importName:u,patternId:u.startsWith(e)?u.slice(e.length).toLowerCase():u.toLowerCase()}));return [...p.map(({importName:u,patternId:f})=>({title:`Add ${u} React Template`,task:c(async(y,R)=>{let d={type:"data",id:makeShortId(),title:"Main",patternId:f,templateId:"react",data:{props:{},slots:{}}},P=Pe({id:f,importName:u,pkgPath:r,initialDemoId:d.id});await Promise.all([writeDemo({dataDir:s,demo:d}),writeKsPatternConfig({dataDir:s,patternId:f,data:P})]);},"task")})),{title:"Updating Nav",task:c(async(u,f)=>{let{byId:y}=await readKsNavConfig({dataDir:s}),R=Object.values(y).find(({path:d,name:P,id:w})=>d?!1:P.toLowerCase()==="patterns"||P.toLowerCase()==="components");await addKsNavItems({dataDir:s,navItems:p.map(({patternId:d})=>({navId:d,navPath:`/pattern/${d}`,navParent:R?.id||"root"}))});},"task")}]},"getTasks")})}),"task")}]},"getTasks")});var H="ks-react-meta";var{pkg:j}=findUpPkgJson(__dirname);log.setupUpdateNotifier({...j,name:j.name,version:j.version});var Q=class r extends RendererWebpackBase{static{c(this,"KnapsackReactRenderer");}assets;babelConfig;demoWrapperPath;disableReactStrictMode;constructor({webpackConfig:e,demoWrapperPath:t=join(__dirname,"./demo-wrapper.mjs"),id:s=rendererIds.react,disableReactStrictMode:a}={}){super({id:s,extension:".jsx",language:"jsx",webpackConfig:e,extraScripts:["@knapsack/renderer-react/client"]}),this.language="jsx",this.assets=[],this.demoWrapperPath=t,this.disableReactStrictMode=a,this.creators=[G];}init=c(async e=>{if(await super.init(e),this.assets=await L(this.outputDir,this.publicPath),!await exists(this.demoWrapperPath))throw new Error(`Could not find demo wrapper at: "${this.demoWrapperPath}"`)},"init");getMeta=c(()=>({id:this.id,title:"React",aliasUse:"optional",aliasTitle:"Named Export",aliasIsJsNamedExport:!0,aliasDescription:"If `export X` was used instead of `export default`, then provide X.",enableDataDemos:!0,enableTemplateDemos:!0,hasSlotsSupport:!0,hasSlotOptionsSupport:!0,version:j.version,hasInferSpecSupport:!0,syntaxHighlightingLanguage:"jsx",hasTemplateSuggestionsSupport:!0,prototypingTemplate:{path:"@knapsack/renderer-react/prototype-template",spec:{isInferred:!1,props:{type:"object",properties:{}},slots:{children:{title:"Children"}}}}}),"getMeta");changeCase=c(e=>pascalCase(e),"changeCase");createWebpackConfig=c(()=>{let e=super.createWebpackConfig();return e.externals={react:"React","react-dom":"ReactDOM"},e},"createWebpackConfig");getJsImports=c(()=>{let e=super.getJsImports();return e.push({type:"extra",importInfo:{type:"default",path:this.demoWrapperPath,name:"DemoWrapper"}},{type:"extra",importInfo:{type:"default",path:join(__dirname,"./error-catcher.mjs"),name:"ErrorCatcher"}}),e},"getJsImports");async prepClientRenderResults({usage:e,demoApp:t,imports:s,renderOptions:{pattern:a,template:o,demo:i}}){let l=this.getJsImports().filter(d=>d.type==="extra"),{imports:m,isDeclaredVarsUnique:n,nameCollisions:p}=this.makeKsJsImportsUnique({imports:[...s,...l]});n||log.error(`${p.join(", ")} are declared multiple times`,{imports:m});let u={demo:i,disableReactStrictMode:this.disableReactStrictMode,neededImports:m,demoWrapperProps:{pattern:a,template:o,demo:i,patternsUsed:m.flatMap(d=>d.type==="pattern-template"?[{patternId:d.patternId,templateId:d.templateId}]:d.type==="pattern-template-demo"?[{patternId:d.patternId,templateId:d.templateId,demoId:d.demoId}]:[])}},f=`
81
+ `;
82
+ }
83
+ __name(renderDemoAppTemplate, "renderDemoAppTemplate");
84
+ async function getUsage(data) {
85
+ const props = Object.keys(data.props || {}).map((key) => {
86
+ const value = data.props[key];
87
+ return {
88
+ key,
89
+ value
90
+ };
91
+ });
92
+ const { templateName, children, extraProps = [] } = data;
93
+ if (!templateName) {
94
+ throw new Error(
95
+ `Cannot getUsage of a React Component when no "templateName" is provided.`
96
+ );
97
+ }
98
+ const attributes = props.map(({ key, value }) => {
99
+ switch (typeof value) {
100
+ case "string":
101
+ if (value.startsWith("(") && value.includes("=>")) {
102
+ return `${key}={${value}}`;
103
+ }
104
+ return `${key}="${value}"`;
105
+ case "boolean":
106
+ return value ? `${key}` : `${key}={${value}}`;
107
+ default:
108
+ return `${key}={${JSON.stringify(value)}}`;
109
+ }
110
+ });
111
+ const extraAttributes = [];
112
+ const slotProps = {};
113
+ extraProps.forEach(({ key, value }) => {
114
+ slotProps[key] = slotProps[key] ?? [];
115
+ slotProps[key].push(value);
116
+ });
117
+ Object.entries(slotProps).forEach(([key, values]) => {
118
+ const value = values.length === 1 ? values[0] : `<>${values.join("\n")}</>`;
119
+ extraAttributes.push(`${key}={${value}}`);
120
+ });
121
+ const result = renderUsageTemplate({
122
+ templateName,
123
+ attributes: [...attributes, ...extraAttributes].join(" "),
124
+ children
125
+ });
126
+ return data.format ? formatCode({
127
+ contents: result,
128
+ path: "x.tsx"
129
+ // doing this to set format language
130
+ }).then((code) => code.trim()).catch(() => result.trim()) : result.trim();
131
+ }
132
+ __name(getUsage, "getUsage");
133
+ async function getDemoAppUsage({
134
+ children,
135
+ imports,
136
+ format
137
+ }) {
138
+ const code = renderDemoAppTemplate({
139
+ children,
140
+ imports
141
+ });
142
+ if (!format) return code;
143
+ return formatCode({
144
+ contents: code,
145
+ path: "x.tsx"
146
+ // doing this to set format language
147
+ });
148
+ }
149
+ __name(getDemoAppUsage, "getDemoAppUsage");
150
+ async function getTypeScriptInfoFromFile({ src }) {
151
+ const tsConfigPath = await findUp("tsconfig.json", {
152
+ cwd: path2.dirname(src)
153
+ });
154
+ const rdTs = await import("react-docgen-typescript");
155
+ const config = {
156
+ shouldExtractLiteralValuesFromEnum: true,
157
+ savePropValueAsString: true,
158
+ skipChildrenPropWithoutDoc: false,
159
+ // In addition to the ones listed here, which had not strangely included these below ~ https://github.com/styleguidist/react-docgen-typescript/blob/287e7012843cb26fed8f4bd8ee24e462c25a1414/src/parser.ts#L308
160
+ customComponentTypes: ["VoidFunctionComponent", "VFC"]
161
+ };
162
+ const parse2 = tsConfigPath ? rdTs.withCustomConfig(tsConfigPath, config).parse : rdTs.withDefaultConfig(config).parse;
163
+ return {
164
+ info: parse2(src)
165
+ };
166
+ }
167
+ __name(getTypeScriptInfoFromFile, "getTypeScriptInfoFromFile");
168
+ function extractSpecFromTypeScriptInfo({
169
+ info: results,
170
+ exportName
171
+ }) {
172
+ try {
173
+ if (!results) return false;
174
+ const spec = {
175
+ props: {
176
+ $schema: "http://json-schema.org/draft-07/schema",
177
+ type: "object",
178
+ required: [],
179
+ properties: {}
180
+ },
181
+ slots: {
182
+ // children: {
183
+ // title: 'children',
184
+ // },
185
+ }
186
+ };
187
+ const isDefaultExport = !exportName || exportName === "default";
188
+ const result = isDefaultExport ? results.pop() : results.find((r) => r.displayName === exportName);
189
+ if (!result) return false;
190
+ const { displayName } = result;
191
+ Object.entries(result?.props || {}).forEach(([propName, propDef]) => {
192
+ const { name, description, defaultValue, required, type, parent } = propDef;
193
+ const propType = type?.name?.replace("| undefined", "").replace(/<.*/g, "").trim();
194
+ switch (propType) {
195
+ case "string":
196
+ spec.props.properties[propName] = {
197
+ type: "string"
198
+ };
199
+ break;
200
+ case "number":
201
+ spec.props.properties[propName] = {
202
+ type: "number"
203
+ };
204
+ break;
205
+ case "boolean":
206
+ case "bool":
207
+ if (defaultValue && "value" in defaultValue) {
208
+ defaultValue.value = defaultValue.value === "true";
209
+ }
210
+ spec.props.properties[propName] = {
211
+ type: "boolean"
212
+ };
213
+ break;
214
+ case "enum":
215
+ spec.props.properties[propName] = {
216
+ type: "string",
217
+ // yes there is a double "value" & yes it is confusing
218
+ enum: [
219
+ // ensure list is unique
220
+ ...new Set(
221
+ type.value.map(({ value }) => removeWrappingDoubleQuotes(value?.trim())).filter(Boolean)
222
+ )
223
+ ]
224
+ };
225
+ break;
226
+ case "ReactNode":
227
+ case "React.ReactNode":
228
+ case "React.ReactElement":
229
+ case "ReactElement":
230
+ spec.slots[propName] = {
231
+ title: propName
232
+ };
233
+ if (description) {
234
+ spec.slots[propName] = {
235
+ ...spec.slots[propName],
236
+ description
237
+ };
238
+ }
239
+ break;
240
+ case "VFC":
241
+ case "FC":
242
+ spec.slots[propName] = {
243
+ title: propName,
244
+ description: description || "A reference to a component",
245
+ isTemplateReference: true,
246
+ allowOnlyOne: true,
247
+ disallowText: true
248
+ };
249
+ break;
250
+ default: {
251
+ if (type?.name?.startsWith("(")) {
252
+ spec.props.properties[propName] = {
253
+ // description: `\`${type.name}\` ${description}`,
254
+ typeof: "function",
255
+ tsType: propDef?.type?.name
256
+ };
257
+ } else if (type?.name?.includes("|")) {
258
+ const options = type.name.split("|").map((enumItem) => removeWrappingDoubleQuotes(enumItem.trim())).filter(Boolean);
259
+ if (options?.length) {
260
+ spec.props.properties[propName] = {
261
+ type: "string",
262
+ // ensuring list is unique
263
+ enum: [...new Set(options)]
264
+ };
265
+ }
266
+ } else {
267
+ spec.props.properties[propName] = {
268
+ tsType: propDef?.type?.name
269
+ };
270
+ }
271
+ }
272
+ }
273
+ if (spec.props.properties[propName]) {
274
+ if (required) spec.props.required.push(propName);
275
+ if (description && !spec.props.properties[propName].description) {
276
+ spec.props.properties[propName].description = description;
277
+ }
278
+ if (defaultValue && "value" in defaultValue) {
279
+ if (isNumberProp(spec.props.properties[propName])) {
280
+ spec.props.properties[propName].default = parseFloat(
281
+ defaultValue.value
282
+ );
283
+ } else {
284
+ spec.props.properties[propName].default = removeWrappingDoubleQuotes(defaultValue.value);
285
+ }
286
+ }
287
+ }
288
+ });
289
+ return spec;
290
+ } catch (error) {
291
+ log.verbose(
292
+ "Could not infer spec from React TypeScript file",
293
+ {
294
+ exportName,
295
+ error
296
+ },
297
+ "react renderer"
298
+ );
299
+ return false;
300
+ }
301
+ }
302
+ __name(extractSpecFromTypeScriptInfo, "extractSpecFromTypeScriptInfo");
303
+ async function getPropTypesInfoFromFile({ src }) {
304
+ const reactDocs = await import("react-docgen");
305
+ const { builtinResolvers } = reactDocs;
306
+ const fileSrc = await readFile(src);
307
+ const { FindExportedDefinitionsResolver } = builtinResolvers;
308
+ const results = reactDocs.parse(fileSrc, {
309
+ resolver: new FindExportedDefinitionsResolver(),
310
+ handlers: null,
311
+ filename: src
312
+ // babelrc: false,
313
+ });
314
+ return {
315
+ info: results
316
+ };
317
+ }
318
+ __name(getPropTypesInfoFromFile, "getPropTypesInfoFromFile");
319
+ function extractSpecFromPropTypesInfo({
320
+ info: results,
321
+ exportName
322
+ }) {
323
+ try {
324
+ const isDefaultExport = !exportName || exportName === "default";
325
+ const result = isDefaultExport ? results.pop() : results.find((r) => r.displayName === exportName);
326
+ const spec = {
327
+ isInferred: true,
328
+ props: {
329
+ $schema: "http://json-schema.org/draft-07/schema",
330
+ type: "object",
331
+ required: [],
332
+ properties: {}
333
+ },
334
+ slots: {}
335
+ };
336
+ Object.entries(result?.props || {}).forEach(([propName, propDef]) => {
337
+ const { required, description, defaultValue } = propDef;
338
+ switch (propDef?.type?.name) {
339
+ case "string":
340
+ spec.props.properties[propName] = {
341
+ type: "string"
342
+ };
343
+ break;
344
+ case "func":
345
+ spec.props.properties[propName] = {
346
+ type: "string"
347
+ };
348
+ break;
349
+ case "bool":
350
+ spec.props.properties[propName] = {
351
+ type: "boolean"
352
+ };
353
+ break;
354
+ case "node":
355
+ spec.slots[propName] = {
356
+ title: propName,
357
+ description
358
+ };
359
+ }
360
+ if (spec.props.properties[propName]) {
361
+ if (required) spec.props.required.push(propName);
362
+ if (description && !spec.props.properties[propName].description) {
363
+ spec.props.properties[propName].description = description;
364
+ }
365
+ if (defaultValue && "value" in defaultValue) {
366
+ spec.props.properties[propName].default = typeof defaultValue.value === "string" ? removeWrappingDoubleQuotes(defaultValue.value) : defaultValue.value;
367
+ }
368
+ }
369
+ });
370
+ return spec;
371
+ } catch (error) {
372
+ log.verbose(
373
+ "Could not infer spec from React PropTypes",
374
+ {
375
+ exportName,
376
+ error
377
+ },
378
+ "react renderer"
379
+ );
380
+ return false;
381
+ }
382
+ }
383
+ __name(extractSpecFromPropTypesInfo, "extractSpecFromPropTypesInfo");
384
+ function cleanUpSpec({
385
+ spec
386
+ }) {
387
+ if (spec === false) return spec;
388
+ Object.entries(spec?.props?.properties || {}).forEach(([propName, prop]) => {
389
+ if (isOptionsProp(prop)) {
390
+ if (!prop.enum.includes(prop.default)) {
391
+ prop.default = void 0;
392
+ }
393
+ }
394
+ });
395
+ return spec;
396
+ }
397
+ __name(cleanUpSpec, "cleanUpSpec");
398
+ async function getReactModuleInfoUncached({
399
+ src: unknownSrc,
400
+ resolveFromDir
401
+ }) {
402
+ const typesInfo = await resolvePath({
403
+ path: unknownSrc,
404
+ resolveFromDir,
405
+ resolveType: "types"
406
+ });
407
+ if (typesInfo.exists) {
408
+ const typeScriptInfo = await getTypeScriptInfoFromFile({
409
+ src: typesInfo.absolutePath
410
+ });
411
+ if (typeScriptInfo) {
412
+ return {
413
+ type: "typescript",
414
+ info: typeScriptInfo.info
415
+ };
416
+ }
417
+ }
418
+ const jsInfo = await resolvePath({
419
+ path: unknownSrc,
420
+ resolveFromDir
421
+ });
422
+ if (!jsInfo.exists) return { type: "unknown" };
423
+ const { ext } = path2.parse(jsInfo.absolutePath);
424
+ switch (ext) {
425
+ case ".jsx": {
426
+ const propTypesInfo = await getPropTypesInfoFromFile({
427
+ src: jsInfo.absolutePath
428
+ });
429
+ return {
430
+ type: "propTypes",
431
+ info: propTypesInfo.info
432
+ };
433
+ }
434
+ case ".ts":
435
+ case ".tsx": {
436
+ const typeScriptInfo = await getTypeScriptInfoFromFile({
437
+ src: jsInfo.absolutePath
438
+ });
439
+ return {
440
+ type: "typescript",
441
+ info: typeScriptInfo.info
442
+ };
443
+ }
444
+ default:
445
+ return {
446
+ type: "unknown"
447
+ };
448
+ }
449
+ }
450
+ __name(getReactModuleInfoUncached, "getReactModuleInfoUncached");
451
+ var getReactModuleInfoCache = /* @__PURE__ */ new Map();
452
+ var clearInferSpecCache = /* @__PURE__ */ __name(() => {
453
+ log.info(`Clearing React TypeScript inferSpec cache...`);
454
+ getReactModuleInfoCache.clear();
455
+ }, "clearInferSpecCache");
456
+ async function getReactModuleInfo(args) {
457
+ const cacheKey = JSON.stringify(args);
458
+ if (!getReactModuleInfoCache.has(cacheKey)) {
459
+ getReactModuleInfoCache.set(cacheKey, getReactModuleInfoUncached(args));
460
+ }
461
+ return getReactModuleInfoCache.get(cacheKey);
462
+ }
463
+ __name(getReactModuleInfo, "getReactModuleInfo");
464
+ async function getReactSpec({
465
+ src,
466
+ exportName,
467
+ resolveFromDir
468
+ }) {
469
+ const reactModuleInfo = await getReactModuleInfo({
470
+ src,
471
+ resolveFromDir
472
+ });
473
+ switch (reactModuleInfo.type) {
474
+ case "typescript":
475
+ return extractSpecFromTypeScriptInfo({
476
+ info: reactModuleInfo.info,
477
+ exportName
478
+ });
479
+ case "propTypes":
480
+ return extractSpecFromPropTypesInfo({
481
+ info: reactModuleInfo.info,
482
+ exportName
483
+ });
484
+ case "unknown":
485
+ default:
486
+ return false;
487
+ }
488
+ }
489
+ __name(getReactSpec, "getReactSpec");
490
+ async function getReactDocs(opt) {
491
+ return cleanUpSpec({
492
+ spec: await getReactSpec(opt)
493
+ });
494
+ }
495
+ __name(getReactDocs, "getReactDocs");
496
+ async function copyReactAssets(distDirAbsolute, publicPath) {
497
+ try {
498
+ const { version: reactVersion } = getModulePkgJson("react");
499
+ const { version: reactDomVersion } = getModulePkgJson("react-dom");
500
+ const reactRoot = path2.dirname(
501
+ __require.resolve("react", {
502
+ paths: [process.cwd()]
503
+ })
504
+ );
505
+ const reactDomRoot = path2.dirname(
506
+ __require.resolve("react-dom", {
507
+ paths: [process.cwd()]
508
+ })
509
+ );
510
+ await Promise.all([
511
+ copy(
512
+ path2.join(reactRoot, "umd/react.development.js"),
513
+ path2.join(distDirAbsolute, `react.development.${reactVersion}.js`)
514
+ ),
515
+ copy(
516
+ path2.join(reactRoot, "umd/react.production.min.js"),
517
+ path2.join(distDirAbsolute, `react.production.min.${reactVersion}.js`)
518
+ ),
519
+ copy(
520
+ path2.join(reactDomRoot, "umd/react-dom.production.min.js"),
521
+ path2.join(
522
+ distDirAbsolute,
523
+ `react-dom.production.min.${reactDomVersion}.js`
524
+ )
525
+ ),
526
+ copy(
527
+ path2.join(reactDomRoot, "umd/react-dom.development.js"),
528
+ path2.join(
529
+ distDirAbsolute,
530
+ `react-dom.development.${reactDomVersion}.js`
531
+ )
532
+ )
533
+ ]);
534
+ const reactFileSuffix = process.env.NODE_ENV === "production" ? "production.min" : "development";
535
+ return [
536
+ path2.join(publicPath, `react.${reactFileSuffix}.${reactVersion}.js`),
537
+ path2.join(
538
+ publicPath,
539
+ `react-dom.${reactFileSuffix}.${reactDomVersion}.js`
540
+ )
541
+ ];
542
+ } catch (error) {
543
+ log.warn(
544
+ 'Error trying to copy "react" and "react-dom" JS files, are they installed? We want to use your exact versions.',
545
+ error,
546
+ "templateRenderer:react"
547
+ );
548
+ process.exit(1);
549
+ }
550
+ }
551
+ __name(copyReactAssets, "copyReactAssets");
552
+
553
+ // src/react-creators.ts
554
+ import { getJsExportNames } from "@knapsack/file-utils";
555
+ import { createCreator, tasks } from "@knapsack/creator-utils";
556
+ import {
557
+ readKsPatternConfigs,
558
+ writeKsPatternConfig,
559
+ readKsNavConfig,
560
+ addKsNavItems,
561
+ writeDemo
562
+ } from "@knapsack/ks-file-utils";
563
+ import { makeShortId } from "@knapsack/utils";
564
+ function isCapitalLetter(char) {
565
+ return char.toUpperCase() === char;
566
+ }
567
+ __name(isCapitalLetter, "isCapitalLetter");
568
+ function startsWithCapitalLetter(str) {
569
+ return isCapitalLetter(str[0]);
570
+ }
571
+ __name(startsWithCapitalLetter, "startsWithCapitalLetter");
572
+ function createPatternData({
573
+ importName,
574
+ id,
575
+ title = importName,
576
+ pkgPath,
577
+ initialDemoId
578
+ }) {
579
+ return {
580
+ id,
581
+ title,
582
+ description: "",
583
+ statuses: {
584
+ main: "ready"
585
+ },
586
+ templates: [
587
+ {
588
+ id: "react",
589
+ title: "react",
590
+ path: pkgPath,
591
+ alias: importName,
592
+ templateLanguageId: "react",
593
+ spec: {
594
+ isInferred: true
595
+ },
596
+ demoIds: [initialDemoId],
597
+ blockIds: []
598
+ }
599
+ ],
600
+ tabs: [
601
+ {
602
+ type: "template",
603
+ id: "react"
604
+ }
605
+ ],
606
+ subPages: []
607
+ };
608
+ }
609
+ __name(createPatternData, "createPatternData");
610
+ var createReactPattern = createCreator({
611
+ id: "react-patterns",
612
+ title: "React Ks Patterns",
613
+ description: "Adds React templates as Knapsack Patterns",
614
+ getQuestions: /* @__PURE__ */ __name(async () => ({
615
+ pkgPath: {
616
+ type: "text",
617
+ title: "Package path"
618
+ },
619
+ importPrefix: {
620
+ type: "text",
621
+ title: "Import Prefix to Remove"
622
+ }
623
+ }), "getQuestions"),
624
+ getTasks: /* @__PURE__ */ __name(async ({ answers: { pkgPath, importPrefix = "" }, config }) => {
625
+ const dataDir = config.dest;
626
+ const currentPatterns = await readKsPatternConfigs({
627
+ dataDir
628
+ });
629
+ const currentReactPatternsImportNames = currentPatterns.reduce(
630
+ (cur, pattern) => {
631
+ const reactTemplates = pattern.templates?.filter((t) => t.templateLanguageId === "react") ?? [];
632
+ cur.push(...reactTemplates.map((t) => t.alias));
633
+ return cur;
634
+ },
635
+ []
636
+ );
637
+ const { exports: allImports } = await getJsExportNames({
638
+ path: pkgPath
639
+ });
640
+ return [
641
+ {
642
+ title: "Pick Imports to add",
643
+ task: /* @__PURE__ */ __name((_, task) => tasks.runSubCreator({
644
+ task,
645
+ config,
646
+ creator: createCreator({
647
+ id: "react-pattern-import-names",
648
+ getQuestions: /* @__PURE__ */ __name(async () => ({
649
+ importNames: {
650
+ type: "choices",
651
+ choices: allImports.filter(
652
+ (importName) => startsWithCapitalLetter(importName) && !currentReactPatternsImportNames.includes(importName)
653
+ ).map((importName) => ({
654
+ value: importName
655
+ }))
656
+ }
657
+ }), "getQuestions"),
658
+ getTasks: /* @__PURE__ */ __name(async ({ answers: { importNames } }) => {
659
+ const patterns = importNames.map((importName) => ({
660
+ importName,
661
+ patternId: importName.startsWith(importPrefix) ? importName.slice(importPrefix.length).toLowerCase() : importName.toLowerCase()
662
+ }));
663
+ return [
664
+ ...patterns.map(
665
+ ({ importName, patternId }) => ({
666
+ title: `Add ${importName} React Template`,
667
+ task: /* @__PURE__ */ __name(async (__, subTask) => {
668
+ const initialDemo = {
669
+ type: "data",
670
+ id: makeShortId(),
671
+ title: "Main",
672
+ patternId,
673
+ templateId: "react",
674
+ data: {
675
+ props: {},
676
+ slots: {}
677
+ }
678
+ };
679
+ const pattern = createPatternData({
680
+ id: patternId,
681
+ importName,
682
+ pkgPath,
683
+ initialDemoId: initialDemo.id
684
+ });
685
+ await Promise.all([
686
+ writeDemo({
687
+ dataDir,
688
+ demo: initialDemo
689
+ }),
690
+ writeKsPatternConfig({
691
+ dataDir,
692
+ patternId,
693
+ data: pattern
694
+ })
695
+ ]);
696
+ }, "task")
697
+ })
698
+ ),
699
+ {
700
+ title: `Updating Nav`,
701
+ task: /* @__PURE__ */ __name(async (__, subTask) => {
702
+ const { byId } = await readKsNavConfig({
703
+ dataDir
704
+ });
705
+ const componentsGroup = Object.values(byId).find(
706
+ ({ path: path3, name, id }) => {
707
+ if (path3) return false;
708
+ if (name.toLowerCase() === "patterns") return true;
709
+ if (name.toLowerCase() === "components") return true;
710
+ return false;
711
+ }
712
+ );
713
+ await addKsNavItems({
714
+ dataDir,
715
+ navItems: patterns.map(({ patternId }) => ({
716
+ navId: patternId,
717
+ navPath: `/pattern/${patternId}`,
718
+ navParent: componentsGroup?.id || "root"
719
+ }))
720
+ });
721
+ }, "task")
722
+ }
723
+ ];
724
+ }, "getTasks")
725
+ })
726
+ }), "task")
727
+ }
728
+ ];
729
+ }, "getTasks")
730
+ });
731
+
732
+ // src/types.ts
733
+ var rendererMetaScriptTagId = "ks-react-meta";
734
+
735
+ // src/renderer-react.ts
736
+ var { pkg } = findUpPkgJson(__dirname);
737
+ log2.setupUpdateNotifier({ ...pkg, name: pkg.name, version: pkg.version });
738
+ var errorCatcherPath = join(__dirname, "./error-catcher.mjs");
739
+ var KnapsackReactRenderer = class _KnapsackReactRenderer extends RendererWebpackBase {
740
+ static {
741
+ __name(this, "KnapsackReactRenderer");
742
+ }
743
+ /**
744
+ * `react.js` & `react-dom.js` root relative paths
745
+ */
746
+ assets;
747
+ babelConfig;
748
+ demoWrapperPath;
749
+ disableReactStrictMode;
750
+ constructor({
751
+ webpackConfig,
752
+ demoWrapperPath = join(__dirname, "./demo-wrapper.mjs"),
753
+ id = rendererIds.react,
754
+ disableReactStrictMode
755
+ } = {}) {
756
+ super({
757
+ id,
758
+ language: "jsx",
759
+ webpackConfig,
760
+ extraScripts: [
761
+ // this is the code in `./client/init.mts`
762
+ "@knapsack/renderer-react/client"
763
+ ],
764
+ codeSrcs: [demoWrapperPath, errorCatcherPath]
765
+ });
766
+ this.language = "jsx";
767
+ this.assets = [];
768
+ this.demoWrapperPath = isAbsolute(demoWrapperPath) ? demoWrapperPath : this.resolvePathSync({
769
+ path: demoWrapperPath,
770
+ resolveFromDir: this.userConfigDir
771
+ }).absolutePath;
772
+ assertFileExists(
773
+ this.demoWrapperPath,
774
+ `Could not find demo wrapper at: "${this.demoWrapperPath}"
775
+ Please adjust setting in "knapsack.config.js" or pass a different path when creating the React Renderer.`
776
+ );
777
+ this.disableReactStrictMode = disableReactStrictMode;
778
+ this.creators = [createReactPattern];
779
+ }
780
+ init = /* @__PURE__ */ __name(async () => {
781
+ this.assets = await copyReactAssets(this.outputDir, this.publicPath);
782
+ if (!await fileExists(this.demoWrapperPath)) {
783
+ throw new Error(
784
+ `Could not find demo wrapper at: "${this.demoWrapperPath}"`
785
+ );
786
+ }
787
+ }, "init");
788
+ getMeta = /* @__PURE__ */ __name(() => ({
789
+ id: this.id,
790
+ title: "React",
791
+ aliasUse: "optional",
792
+ aliasTitle: "Named Export",
793
+ aliasIsJsNamedExport: true,
794
+ aliasDescription: "If `export X` was used instead of `export default`, then provide X.",
795
+ enableDataDemos: true,
796
+ enableTemplateDemos: true,
797
+ hasSlotsSupport: true,
798
+ hasSlotOptionsSupport: true,
799
+ version: pkg.version,
800
+ hasInferSpecSupport: true,
801
+ syntaxHighlightingLanguage: "jsx",
802
+ hasTemplateSuggestionsSupport: true,
803
+ prototypingTemplate: {
804
+ path: "@knapsack/renderer-react/prototype-template",
805
+ spec: {
806
+ isInferred: false,
807
+ props: {
808
+ type: "object",
809
+ properties: {}
810
+ },
811
+ slots: {
812
+ children: { title: "Children" }
813
+ }
814
+ }
815
+ }
816
+ }), "getMeta");
817
+ changeCase = /* @__PURE__ */ __name((str) => pascalCase(str), "changeCase");
818
+ createWebpackConfig = /* @__PURE__ */ __name(() => {
819
+ const config = super.createWebpackConfig();
820
+ config.externals = {
821
+ react: "React",
822
+ "react-dom": "ReactDOM"
823
+ };
824
+ return config;
825
+ }, "createWebpackConfig");
826
+ async prepClientRenderResults({
827
+ usage,
828
+ demoApp,
829
+ importMap,
830
+ renderOptions: { demo, state, patternId, templateId }
831
+ }) {
832
+ const meta = {
833
+ demo,
834
+ disableReactStrictMode: this.disableReactStrictMode,
835
+ neededImportsByPath: convertImportMapToNeededImportsByPath(importMap),
836
+ extraImports: {
837
+ DemoWrapper: {
838
+ type: "default",
839
+ path: this.demoWrapperPath,
840
+ name: "DemoWrapper"
841
+ },
842
+ ErrorCatcher: {
843
+ type: "default",
844
+ path: errorCatcherPath,
845
+ name: "ErrorCatcher"
846
+ }
847
+ },
848
+ demoWrapperProps: {
849
+ patternId,
850
+ templateId,
851
+ demo
852
+ }
853
+ };
854
+ let code = `
27
855
  window.knapsack = window.knapsack || {};
28
- window.knapsack.getDemoApp = ({ ${m.map(d=>d.importInfo.name).join(", ")} }) => {
29
- ${t}
30
- return ${O}
856
+ window.knapsack.getDemoApp = ({ ${[...importMap.keys()].join(", ")} }) => {
857
+ ${demoApp}
858
+ return ${demoAppName}
31
859
  }
32
- `,y="";try{f=await babelCodeForBrowser({code:f});}catch(d){console.log(f),console.log("---original code before babel error ^---"),console.trace(d.message),f=`console.error(${JSON.stringify(d.message)});`,y=`<pre><code>${d.message}</code></pre>`;}let R=`
33
- <script type="application/json" id="${H}">${JSON.stringify(u)}</script>
34
- <script type="application/javascript">${f}</script>
860
+ `;
861
+ let errorHtmlMsg = "";
862
+ try {
863
+ code = await babelCodeForBrowser({ code });
864
+ } catch (e) {
865
+ console.log(code);
866
+ console.log("---original code before babel error ^---");
867
+ console.trace(e.message);
868
+ code = `console.error(${JSON.stringify(e.message)});`;
869
+ errorHtmlMsg = `<pre><code>${e.message}</code></pre>`;
870
+ }
871
+ const html = `
872
+ <script type="application/json" id="${rendererMetaScriptTagId}">${JSON.stringify(
873
+ meta
874
+ )}</script>
875
+ <script type="application/javascript">${code}</script>
35
876
  <div id="render-root" class="knapsack-pattern-direct-parent" data-dev-note="Knapsack React Template Wrapper"></div>
36
- ${this.assets.map(d=>`<script src="${d}"></script>`).join(`
37
- `)}
38
- ${this.createHtmlTagsForAssetPaths({assets:this.getWebpackAssetPaths(),scriptTagsAreAsync:!1})}
39
- ${y}
40
- `;return {ok:!y,html:await formatCode({contents:R,path:"x.html"}),usage:e,templateLanguage:this.language}}render=c(async e=>{if(e.demo?.type==="template"){let t=[5,10,20,50,100,1e3,1e3],s,a=0;for(;;)try{let{absolutePath:p,exists:u}=await this.resolvePath(e.demo.templateInfo.path);if(!u)throw new Error(`Template demo file does not exist: ${p}`);s=p;break}catch(p){let u=t[a];if(!u)throw new Error(p);a+=1,await Ie(u);}let[o,{usage:i,imports:l}]=await Promise.all([readFile(s),this.getUsageAndImports(e)]),m=await v({children:i});return await this.prepClientRenderResults({usage:o,demoApp:m,imports:l,renderOptions:e})}if(e.demo?.type==="data"){let{usage:t,imports:s}=await this.getUsageAndImports(e),{code:a}=this.createJsImportCodeBlock({imports:s}),[o,i]=await Promise.all([v({children:t,imports:a,format:!0}),v({children:t})]);return this.prepClientRenderResults({demoApp:i,usage:o,imports:s,renderOptions:e})}},"render");getUsageAndImports=c(async({pattern:e,template:t,patternManifest:s,demo:a})=>{if(a?.type&&a.type==="data"){let{data:{props:o,slots:i,slotsOptionsComputed:l}}=a,m=this.getJsImport({patternId:e.id,templateId:t.id});if(!m)throw new Error(`Could not find import for pattern-template: ${e.id}-${t.id}`);let{type:n,name:p}=m.importInfo,u=[m],f=[],y=[];if(i){let d=Object.keys(i);(await Promise.all(d.map(async w=>{let T=i[w],D=await Promise.all(T.filter(g=>!(g.type!=="text"&&(!g.patternId||!g.templateId||g.type==="template-demo"&&!g.demoId))).map(async g=>{if(g.type==="text")return T.length===1&&w!=="children"?`\`${g.text}\``:g.text;let I=s.getPattern(g.patternId),b=I.templates.find(k=>k.id===g.templateId);if(g.type==="template-reference"){let{usage:k,imports:E}=await this.getUsageAndImports({pattern:I,template:b,patternManifest:s});return u.push(...E),k}if(g.type==="template-demo"){let{usage:k,imports:E}=await this.getUsageAndImports({pattern:I,template:b,demo:g.demo||this.patterns.demosById[g.demoId],patternManifest:s});return u.push(...E),k}throw new Error(`Unknown slot item: ${JSON.stringify(g)}`)}));return {slotName:w,slotItemsUsages:D}}))).forEach(({slotName:w,slotItemsUsages:T})=>{let D=l?.[w],{openTag:g,closeTag:I}=createSlotOptionsHtmlTags({slotOptionsComputed:D,classAttributeName:"className",stylesValueType:"object"});g&&(w==="children"?f.push(g):y.push({key:w,value:g})),T.forEach(b=>{w==="children"?f.push(b):y.push({key:w,value:b});}),I&&(w==="children"?f.push(I):y.push({key:w,value:I}));});}return {usage:await M({templateName:p,props:o,children:f.join(`
41
- `),extraProps:y}),imports:u}}if(a?.type&&a.type==="template"){let o=this.getJsImport({patternId:e.id,templateId:t.id,demoId:a.id});if(!o)throw new Error(`Could not find import for pattern-template-demo: ${e.id}-${t.id}-${a.id}`);let{type:i,name:l}=o.importInfo;return {usage:await M({templateName:l}),imports:[o]}}if(!a){let o=this.getJsImport({patternId:e.id,templateId:t.id});if(!o)throw new Error(`Could not find import for pattern-template: ${e.id}-${t.id}`);let{type:i,name:l}=o.importInfo;return {usage:l,imports:[o]}}throw new Error(`Unhandled demo type for ${e.id}-${t.id}: ${JSON.stringify(a)}`)},"getUsageAndImports");inferSpec=c(async({template:e,templatePath:t})=>{let s=await B({src:e.path,exportName:e.alias||"default",resolveFromDir:this.config.data});if(s!==!1){let a=Object.keys(s?.props?.properties||{}).length,o=Object.keys(s?.slots||{}).length;if(a===0&&o===0)return !1}return s},"inferSpec");watch=c(async e=>{super.watch(e),knapsackEvents.onPatternTemplateChanged(()=>{V();});},"watch");getTemplateMeta=c(async({pattern:e,template:t})=>{let s=[];if(t?.spec?.props){let a=JSON.parse(JSON.stringify(t.spec.props));t?.spec?.slots&&Object.entries(t.spec.slots).forEach(([i,l])=>{a.properties[i]={typeof:"function",tsType:"React.ReactNode",description:l.allowedPatternIds?`${l.description}. Only use: ${l.allowedPatternIds.join(", ")}`:l.description},a.required=a.required??[],l.isRequired&&a.required.push(i);});let o=await r.convertSchemaToTypeScriptDefs({schema:a,title:`${this.changeCase(e.id)}Props`,description:`[Knapsack Docs](http://localhost:3999/pattern/${e.id}/${t.id})`,patternId:e.id,templateId:t.id,postBanner:"import * as React from 'react';"});s.push({contents:o,encoding:"utf8",path:`${e.id}.${t.id}.spec.d.ts`}),s.push({contents:JSON.stringify(a,null," "),encoding:"utf8",path:`${e.id}.${t.id}.spec.json`});}return s},"getTemplateMeta");alterTemplateMetaFiles=c(async({files:e,metaDir:t})=>{let s=[],a=".spec.d.ts";return e.forEach(o=>{if(o.path.endsWith(a)){let{base:i}=parse(o.path),[l,m]=i.split("."),n=m===this.id,p=this.changeCase(`${l}Props`),u=this.changeCase(`${l}-${m}Props`);s.push(`export { ${n?p:`${p} as ${u}`} } from './${relative(t,o.path).replace(".d.ts","")}';`);}}),s.push(""),[...e,{contents:s.join(`
42
- `),encoding:"utf8",path:join(t,"react.d.ts")}]},"alterTemplateMetaFiles");getTemplateSuggestions=c(async({newPath:e})=>{let{data:t}=this.patterns.userConfig,{allTemplateDemos:s,allTemplates:a}=this.getMyTemplates(),o=[...s,...a].map(({path:n,alias:p})=>({path:n,alias:p||"default"})),i=[...new Set([e,...o.map(({path:n})=>n)])];return {suggestions:(await Promise.all(i.map(async n=>{if(!n)return [];try{let{exports:p,errorMsg:u}=await getJsExportNames({path:n,resolveFromDir:t,pkgPathAliases:this.pkgPathAliases});if(u)throw new Error(u);return p.filter(f=>f==="default"||isFirstLetterCapital(f)).map(f=>({alias:f,path:n}))}catch(p){return log.verbose(`Error getting import names for ${n}: ${p.message}`,null,this.logPrefix),[]}})).then(n=>n.flat())).filter(n=>!o.find(p=>p.alias===n.alias&&p.path===n.path))}},"getTemplateSuggestions")};
43
-
44
- export { Q as KnapsackReactRenderer };
45
- //# sourceMappingURL=index.mjs.map
877
+ ${this.assets.map((asset) => `<script src="${asset}"></script>`).join("\n")}
878
+ ${RendererWebpackBase.createHtmlTagsForAssetPaths({
879
+ assets: this.getWebpackAssetPaths(),
880
+ // we need the scripts to finish adding methods to the global knapsack object synchronously before the client-side code runs that is in the <script> tag below
881
+ scriptTagsAreAsync: false
882
+ })}
883
+ ${errorHtmlMsg}
884
+ `;
885
+ return {
886
+ ok: !errorHtmlMsg,
887
+ html: await formatCode2({
888
+ contents: html,
889
+ path: "x.html"
890
+ // doing this to set format language
891
+ }),
892
+ usage,
893
+ templateLanguage: this.language
894
+ };
895
+ }
896
+ render = /* @__PURE__ */ __name(async (opt) => {
897
+ if (opt.demo?.type === "template") {
898
+ const waits = [5, 10, 20, 50, 100, 1e3, 1e3];
899
+ let templateDemoPath;
900
+ let attempt = 0;
901
+ while (true) {
902
+ try {
903
+ const { absolutePath, exists } = await this.resolvePath(
904
+ opt.demo.templateInfo.path
905
+ );
906
+ if (!exists) {
907
+ throw new Error(
908
+ `Template demo file does not exist: ${absolutePath}`
909
+ );
910
+ }
911
+ templateDemoPath = absolutePath;
912
+ break;
913
+ } catch (e) {
914
+ const waitTime = waits[attempt];
915
+ if (!waitTime) {
916
+ throw new Error(e);
917
+ }
918
+ attempt += 1;
919
+ await sleep(waitTime);
920
+ }
921
+ }
922
+ const [templateFileContents, { usage, importMap }] = await Promise.all([
923
+ readFile2(templateDemoPath),
924
+ this.getUsageAndImports({
925
+ ...opt,
926
+ importMap: /* @__PURE__ */ new Map()
927
+ })
928
+ ]);
929
+ const demoApp = await getDemoAppUsage({
930
+ children: usage
931
+ });
932
+ const results = await this.prepClientRenderResults({
933
+ usage: templateFileContents,
934
+ demoApp,
935
+ renderOptions: opt,
936
+ importMap
937
+ });
938
+ return results;
939
+ }
940
+ if (opt.demo?.type === "data") {
941
+ const { usage, importMap } = await this.getUsageAndImports({
942
+ ...opt,
943
+ importMap: /* @__PURE__ */ new Map()
944
+ });
945
+ const importCode = RendererWebpackBase.createJsImportCodeBlock({
946
+ importMap
947
+ });
948
+ const [demoAppUsage, demoApp] = await Promise.all([
949
+ getDemoAppUsage({
950
+ children: usage,
951
+ imports: importCode,
952
+ format: true
953
+ }),
954
+ getDemoAppUsage({
955
+ children: usage
956
+ })
957
+ ]);
958
+ return this.prepClientRenderResults({
959
+ demoApp,
960
+ usage: demoAppUsage,
961
+ renderOptions: opt,
962
+ importMap
963
+ });
964
+ }
965
+ }, "render");
966
+ getUsageAndImports = /* @__PURE__ */ __name(async ({
967
+ patternId,
968
+ templateId,
969
+ demo,
970
+ state,
971
+ importMap
972
+ }) => {
973
+ if (!demo) {
974
+ throw new Error(
975
+ `No demo provided while rendering ${patternId} ${templateId}`
976
+ );
977
+ }
978
+ const pattern = state.patterns[patternId];
979
+ if (!pattern) {
980
+ throw new Error(`Could not find pattern: ${patternId}`);
981
+ }
982
+ if (demo.type === "data") {
983
+ const template = pattern.templates.find((t) => t.id === templateId);
984
+ if (!template) {
985
+ throw new Error(`Could not find template: ${templateId}`);
986
+ }
987
+ const {
988
+ data: { props, slots, slotsOptionsComputed }
989
+ } = demo;
990
+ const { name: templateName } = await this.addUniqueValueToImportMap({
991
+ importMap,
992
+ path: template.path,
993
+ alias: template.alias || "default"
994
+ });
995
+ const children = [];
996
+ const extraProps = [];
997
+ if (slots) {
998
+ const slotNames = Object.keys(slots);
999
+ const slotUsages = await Promise.all(
1000
+ slotNames.map(async (slotName) => {
1001
+ const slotItems = slots[slotName];
1002
+ const slotItemsUsages = await Promise.all(
1003
+ slotItems.filter((slotItem) => {
1004
+ if (!slotItem) return false;
1005
+ if (slotItem.type !== "text") {
1006
+ if (!slotItem.patternId) return false;
1007
+ if (!slotItem.templateId) return false;
1008
+ if (slotItem.type === "template-demo" && !slotItem.demoId) {
1009
+ return false;
1010
+ }
1011
+ }
1012
+ return true;
1013
+ }).map(async (slotItem) => {
1014
+ if (slotItem.type === "text") {
1015
+ if (slotItems.length === 1 && slotName !== "children") {
1016
+ return `\`${slotItem.text}\``;
1017
+ }
1018
+ return slotItem.text;
1019
+ }
1020
+ if (slotItem.type === "template-reference") {
1021
+ const slottedTemplate = state.patterns[slotItem.patternId]?.templates.find((t) => t.id === slotItem.templateId);
1022
+ if (!slottedTemplate) {
1023
+ throw new Error(
1024
+ `Could not find slotted template: ${slotItem.patternId} ${slotItem.templateId}`
1025
+ );
1026
+ }
1027
+ const templateRefImport = await this.addUniqueValueToImportMap({
1028
+ importMap,
1029
+ path: slottedTemplate.path,
1030
+ alias: slottedTemplate.alias || "default"
1031
+ });
1032
+ return templateRefImport.name;
1033
+ }
1034
+ if (slotItem.type === "template-demo") {
1035
+ const thisDemo = slotItem.demo;
1036
+ if (!thisDemo) {
1037
+ throw new Error(
1038
+ `Could not find slotted template demo ${JSON.stringify(
1039
+ slotItem
1040
+ )}`
1041
+ );
1042
+ }
1043
+ const { usage: usage2 } = await this.getUsageAndImports({
1044
+ patternId: thisDemo.patternId,
1045
+ templateId: thisDemo.templateId,
1046
+ demo: thisDemo,
1047
+ state,
1048
+ importMap
1049
+ });
1050
+ return usage2;
1051
+ }
1052
+ const _exhaustiveCheck2 = slotItem;
1053
+ throw new Error(
1054
+ `Unknown slot item: ${JSON.stringify(slotItem)}`
1055
+ );
1056
+ })
1057
+ );
1058
+ return {
1059
+ slotName,
1060
+ slotItemsUsages
1061
+ };
1062
+ })
1063
+ );
1064
+ slotUsages.forEach(({ slotName, slotItemsUsages }) => {
1065
+ const slotOptionsComputed = slotsOptionsComputed?.[slotName];
1066
+ const { openTag, closeTag } = createSlotOptionsHtmlTags({
1067
+ slotOptionsComputed,
1068
+ classAttributeName: "className",
1069
+ stylesValueType: "object"
1070
+ });
1071
+ if (openTag) {
1072
+ if (slotName === "children") {
1073
+ children.push(openTag);
1074
+ } else {
1075
+ extraProps.push({
1076
+ key: slotName,
1077
+ value: openTag
1078
+ });
1079
+ }
1080
+ }
1081
+ slotItemsUsages.forEach((usage2) => {
1082
+ if (slotName === "children") {
1083
+ children.push(usage2);
1084
+ } else {
1085
+ extraProps.push({
1086
+ key: slotName,
1087
+ value: usage2
1088
+ });
1089
+ }
1090
+ });
1091
+ if (closeTag) {
1092
+ if (slotName === "children") {
1093
+ children.push(closeTag);
1094
+ } else {
1095
+ extraProps.push({
1096
+ key: slotName,
1097
+ value: closeTag
1098
+ });
1099
+ }
1100
+ }
1101
+ });
1102
+ }
1103
+ const usage = await getUsage({
1104
+ templateName,
1105
+ props,
1106
+ children: children.join("\n"),
1107
+ extraProps
1108
+ });
1109
+ return {
1110
+ usage,
1111
+ importMap
1112
+ };
1113
+ }
1114
+ if (demo.type === "template") {
1115
+ const { templateInfo } = demo;
1116
+ const { name: templateName } = await this.addUniqueValueToImportMap({
1117
+ importMap,
1118
+ path: templateInfo.path,
1119
+ alias: templateInfo.alias || "default"
1120
+ });
1121
+ const usage = await getUsage({ templateName });
1122
+ return {
1123
+ usage,
1124
+ importMap
1125
+ };
1126
+ }
1127
+ const _exhaustiveCheck = demo;
1128
+ throw new Error(
1129
+ `Unhandled demo type for ${patternId}-${templateId}: ${JSON.stringify(
1130
+ demo
1131
+ )}`
1132
+ );
1133
+ }, "getUsageAndImports");
1134
+ inferSpec = /* @__PURE__ */ __name(async ({
1135
+ template,
1136
+ templatePath
1137
+ }) => {
1138
+ const spec = await getReactDocs({
1139
+ src: template.path,
1140
+ exportName: template.alias || "default",
1141
+ resolveFromDir: this.config.data
1142
+ });
1143
+ if (spec !== false) {
1144
+ const totalProps = Object.keys(spec?.props?.properties || {}).length;
1145
+ const totalSlots = Object.keys(spec?.slots || {}).length;
1146
+ if (totalProps === 0 && totalSlots === 0) {
1147
+ return false;
1148
+ }
1149
+ }
1150
+ return spec;
1151
+ }, "inferSpec");
1152
+ watch = /* @__PURE__ */ __name(async () => {
1153
+ super.watch();
1154
+ knapsackEvents.onPatternTemplateChanged(() => {
1155
+ clearInferSpecCache();
1156
+ });
1157
+ }, "watch");
1158
+ getTemplateMeta = /* @__PURE__ */ __name(async ({
1159
+ pattern,
1160
+ template
1161
+ }) => {
1162
+ const files = [];
1163
+ if (template?.spec?.props) {
1164
+ const schema = JSON.parse(JSON.stringify(template.spec.props));
1165
+ if (template?.spec?.slots) {
1166
+ Object.entries(template.spec.slots).forEach(([id, info]) => {
1167
+ schema.properties[id] = {
1168
+ typeof: "function",
1169
+ tsType: "React.ReactNode",
1170
+ description: info.allowedPatternIds ? `${info.description}. Only use: ${info.allowedPatternIds.join(
1171
+ ", "
1172
+ )}` : info.description
1173
+ };
1174
+ schema.required = schema.required ?? [];
1175
+ if (info.isRequired) schema.required.push(id);
1176
+ });
1177
+ }
1178
+ const typeDefs = await _KnapsackReactRenderer.convertSchemaToTypeScriptDefs({
1179
+ schema,
1180
+ title: `${this.changeCase(pattern.id)}Props`,
1181
+ // @todo pull in base url
1182
+ description: `[Knapsack Docs](http://localhost:3999/pattern/${pattern.id}/${template.id})`,
1183
+ patternId: pattern.id,
1184
+ templateId: template.id,
1185
+ postBanner: `import * as React from 'react';`
1186
+ });
1187
+ files.push({
1188
+ contents: typeDefs,
1189
+ encoding: "utf8",
1190
+ path: `${pattern.id}.${template.id}.spec.d.ts`
1191
+ });
1192
+ files.push({
1193
+ contents: JSON.stringify(schema, null, " "),
1194
+ encoding: "utf8",
1195
+ path: `${pattern.id}.${template.id}.spec.json`
1196
+ });
1197
+ }
1198
+ return files;
1199
+ }, "getTemplateMeta");
1200
+ alterTemplateMetaFiles = /* @__PURE__ */ __name(async ({
1201
+ files,
1202
+ metaDir
1203
+ }) => {
1204
+ const imports = [];
1205
+ const ext = ".spec.d.ts";
1206
+ files.forEach((file) => {
1207
+ if (file.path.endsWith(ext)) {
1208
+ const { base } = parse(file.path);
1209
+ const [patternId, templateId] = base.split(".");
1210
+ const isFirst = templateId === this.id;
1211
+ const exportName = this.changeCase(`${patternId}Props`);
1212
+ const exportNameWithTemplateId = this.changeCase(
1213
+ `${patternId}-${templateId}Props`
1214
+ );
1215
+ imports.push(
1216
+ `export { ${isFirst ? exportName : `${exportName} as ${exportNameWithTemplateId}`} } from './${relative(metaDir, file.path).replace(".d.ts", "")}';`
1217
+ );
1218
+ }
1219
+ });
1220
+ imports.push("");
1221
+ return [
1222
+ ...files,
1223
+ {
1224
+ contents: imports.join("\n"),
1225
+ encoding: "utf8",
1226
+ path: join(metaDir, "react.d.ts")
1227
+ }
1228
+ ];
1229
+ }, "alterTemplateMetaFiles");
1230
+ getTemplateSuggestions = /* @__PURE__ */ __name(async ({
1231
+ newPath,
1232
+ state
1233
+ }) => {
1234
+ const usedSuggestions = Object.values(state.patterns).reduce(
1235
+ (acc, { templateDemos, templates }) => {
1236
+ templates.forEach(({ path: path3, alias, templateLanguageId }) => {
1237
+ if (templateLanguageId !== this.id) return;
1238
+ acc.push({ path: path3, alias });
1239
+ });
1240
+ templateDemos.forEach(
1241
+ ({ templateInfo: { path: path3, alias }, templateLanguageId }) => {
1242
+ if (templateLanguageId !== this.id) return;
1243
+ acc.push({ path: path3, alias });
1244
+ }
1245
+ );
1246
+ return acc;
1247
+ },
1248
+ []
1249
+ );
1250
+ const codeSrcs = new Set(this.getCodeSrcs());
1251
+ codeSrcs.delete(this.demoWrapperPath);
1252
+ codeSrcs.delete(errorCatcherPath);
1253
+ const allPaths = [
1254
+ .../* @__PURE__ */ new Set([
1255
+ newPath,
1256
+ ...Object.keys(this.pkgPathAliases || {}),
1257
+ ...codeSrcs
1258
+ ])
1259
+ ];
1260
+ const allSuggestions = await Promise.all(
1261
+ allPaths.map(async (path3) => {
1262
+ if (!path3) return [];
1263
+ try {
1264
+ const { exports, errorMsg } = await getJsExportNames2({
1265
+ path: path3,
1266
+ resolveFromDir: this.dataDir,
1267
+ pkgPathAliases: this.pkgPathAliases
1268
+ });
1269
+ if (errorMsg) {
1270
+ throw new Error(errorMsg);
1271
+ }
1272
+ return exports.filter((e) => e === "default" || isFirstLetterCapital(e)).map((name) => ({
1273
+ alias: name,
1274
+ path: path3
1275
+ }));
1276
+ } catch (e) {
1277
+ log2.verbose(
1278
+ `Error getting import names for ${path3}: ${e.message}`,
1279
+ null,
1280
+ this.logPrefix
1281
+ );
1282
+ return [];
1283
+ }
1284
+ })
1285
+ ).then((results) => {
1286
+ return results.flat();
1287
+ });
1288
+ const suggestions = allSuggestions.filter((s) => {
1289
+ return !usedSuggestions.find(
1290
+ (us) => us.alias === s.alias && us.path === s.path
1291
+ );
1292
+ });
1293
+ return {
1294
+ suggestions
1295
+ };
1296
+ }, "getTemplateSuggestions");
1297
+ };
1298
+ export {
1299
+ KnapsackReactRenderer
1300
+ };
46
1301
  //# sourceMappingURL=index.mjs.map