|
| 1 | +import type { ExternalScript, Script } from 'third-party-capital' |
| 2 | +import { genImport } from 'knitwork' |
| 3 | +import type { HeadEntryOptions } from '@unhead/vue' |
| 4 | +import type { TpcDescriptor } from './generateTpcScripts' |
| 5 | + |
| 6 | +const HEAD_VAR = '__head' |
| 7 | +const INJECTHEAD_CODE = `const ${HEAD_VAR} = injectHead()` |
| 8 | + |
| 9 | +export async function generateTpcContent(input: TpcDescriptor) { |
| 10 | + if (!input.tpcData.scripts) |
| 11 | + throw new Error('input.data has no scripts !') |
| 12 | + |
| 13 | + // replace all empty spaces with nothing |
| 14 | + const titleKey = input.label.replace(/\s/g, '') |
| 15 | + |
| 16 | + const mainScript = input.tpcData.scripts?.find(({ key }) => key === input.tpcKey) as ExternalScript |
| 17 | + |
| 18 | + if (!mainScript) |
| 19 | + throw new Error(`no main script found for ${input.tpcKey} in third-party-capital`) |
| 20 | + |
| 21 | + const mainScriptOptions = getScriptInputOption(mainScript) |
| 22 | + |
| 23 | + const imports = new Set<string>([ |
| 24 | + 'import { withQuery } from \'ufo\'', |
| 25 | + 'import { useRegistryScript } from \'#nuxt-scripts-utils\'', |
| 26 | + 'import type { RegistryScriptInput } from \'#nuxt-scripts\'', |
| 27 | + `import type { ${input.tpcTypeImport} } from 'third-party-capital'`, |
| 28 | + ]) |
| 29 | + |
| 30 | + const chunks: string[] = [] |
| 31 | + const functionBody: string[] = [] |
| 32 | + |
| 33 | + const hasParams = mainScript.params?.length |
| 34 | + |
| 35 | + if (hasParams) { |
| 36 | + imports.add(genImport('#nuxt-scripts-validator', ['object', 'string'])) |
| 37 | + // need schema validation from tpc |
| 38 | + chunks.push(`export const ${titleKey}Options = object({${mainScript.params?.map(p => `${p}: string()`)}})`) |
| 39 | + } |
| 40 | + |
| 41 | + chunks.push(` |
| 42 | +declare global { |
| 43 | + interface Window extends ${input.tpcTypeImport} {} |
| 44 | +}`) |
| 45 | + |
| 46 | + const clientInitCode: string[] = [] |
| 47 | + |
| 48 | + if (input.tpcData.stylesheets) { |
| 49 | + if (!functionBody.includes(INJECTHEAD_CODE)) { |
| 50 | + functionBody.unshift(INJECTHEAD_CODE) |
| 51 | + } |
| 52 | + functionBody.push(`${HEAD_VAR}.push({link: ${JSON.stringify(input.tpcData.stylesheets.map(s => ({ ref: 'stylesheet', href: s })))}})`) |
| 53 | + } |
| 54 | + |
| 55 | + for (const script of input.tpcData.scripts) { |
| 56 | + if ('code' in script) |
| 57 | + clientInitCode.push(replaceTokenToRuntime(script.code)) |
| 58 | + |
| 59 | + if (script === mainScript) |
| 60 | + continue |
| 61 | + |
| 62 | + if ('url' in script) { |
| 63 | + functionBody.push(`${HEAD_VAR}.push({scripts:{ async: true, src: ${script.url} }},${JSON.stringify(getScriptInputOption(script))})`) |
| 64 | + } |
| 65 | + } |
| 66 | + |
| 67 | + chunks.push(`export type ${titleKey}Input = RegistryScriptInput${hasParams ? `<typeof ${titleKey}Options>` : ''}`) |
| 68 | + |
| 69 | + chunks.push(` |
| 70 | +export function ${input.registry.import!.name}<T extends ${input.tpcTypeImport}>(_options?: ${titleKey}Input) { |
| 71 | +${functionBody.join('\n')} |
| 72 | + return useRegistryScript${hasParams ? `<T, typeof ${titleKey}Options>` : ''}(_options?.key || '${input.key}', options => ({ |
| 73 | + scriptInput: { |
| 74 | + src: withQuery('${mainScript.url}', {${mainScript.params?.map(p => `${p}: options?.${p}`)}}) |
| 75 | + }, |
| 76 | + schema: import.meta.dev ? undefined : ${titleKey}Options, |
| 77 | + scriptOptions: { |
| 78 | + use: ${input.options.scriptOptions!.use!.toString()}, |
| 79 | + stub: import.meta.client ? undefined : ${input.options.scriptOptions!.stub!.toString()}, |
| 80 | + ${input.options.scriptOptions?.performanceMarkFeature ? `performanceMarkFeature: ${JSON.stringify(input.options.scriptOptions?.performanceMarkFeature)},` : ''} |
| 81 | + ${mainScriptOptions ? `...(${JSON.stringify(mainScriptOptions)})` : ''} |
| 82 | + }, |
| 83 | + // eslint-disable-next-line |
| 84 | + ${clientInitCode.length ? `clientInit: import.meta.server ? undefined : () => {${clientInitCode.join('\n')}},` : ''} |
| 85 | + }), _options) |
| 86 | +}`) |
| 87 | + |
| 88 | + chunks.unshift(...Array.from(imports)) |
| 89 | + chunks.unshift('// WARNING: This file is automatically generated, do not manually modify.') |
| 90 | + return chunks.join('\n') |
| 91 | +} |
| 92 | + |
| 93 | +function replaceTokenToRuntime(code: string) { |
| 94 | + return code.split(';').map(c => c.replaceAll(/'?\{\{(.*?)\}\}'?/g, 'options.$1')).join(';') |
| 95 | +} |
| 96 | + |
| 97 | +function getScriptInputOption(script: Script): HeadEntryOptions | undefined { |
| 98 | + if (script.location === 'body') { |
| 99 | + if (script.action === 'append') { |
| 100 | + return { tagPosition: 'bodyClose' } |
| 101 | + } |
| 102 | + return { tagPosition: 'bodyOpen' } |
| 103 | + } |
| 104 | + |
| 105 | + if (script.action === 'append') { |
| 106 | + return { tagPriority: 1 } |
| 107 | + } |
| 108 | +} |
0 commit comments