Setting the file. One moment.
Address Templates · Wix Headless Templates · wix/skills · Skills Docs
ContentsBack to the top of the page forms/tooling/address-templates.mjs
forms/tooling/ address-templates.mjs
JavaScript · 96 lines · 6 KB
13 // Run by hand when Wix changes any of them; the output is committed. Needs npm and the network,
14 // no Wix login:
15 // node templates/forms/tooling/address-templates.mjs
16 import { execFileSync } from "node:child_process" ;
17 import { mkdtempSync, readFileSync, writeFileSync } from "node:fs" ;
18 import { tmpdir } from "node:os" ;
19 import { dirname, join, resolve } from "node:path" ;
20 import { fileURLToPath } from "node:url" ;
21
22 const HERE = dirname ( fileURLToPath ( import . meta .url));
23 const OUT = resolve ( HERE , ".." , "app" , "wix" , "forms" , "address-templates.generated.ts" );
24
25 const tmp = mkdtempSync ( join ( tmpdir (), "wix-address-" ));
26 const pack = ( name ) => {
27 const file = execFileSync ( "npm" , [ "pack" , name, "--silent" ], { cwd: tmp, encoding: "utf8" }). trim (). split ( " \n " ). pop ();
28 const dir = join (tmp, file. replace ( / \. tgz $ / , "" ));
29 execFileSync ( "mkdir" , [ "-p" , dir]);
30 execFileSync ( "tar" , [ "xzf" , join (tmp, file), "-C" , dir]);
31 return join (dir, "package" );
32 };
33
34 // 1 · country → template, from Wix's own headless form components. The module imports the SDK at
35 // the top, so its two constants are read from the source text rather than by importing it.
36 const hf = pack ( "@wix/headless-forms" );
37 const hfVersion = JSON . parse ( readFileSync ( join (hf, "package.json" ), "utf8" )).version;
38 const addressForms = readFileSync ( join (hf, "dist" , "services" , "utils" , "address-forms.js" ), "utf8" );
39 const mapSource = addressForms. match ( /ADDRESS_FORM_ID_BY_COUNTRY \s * = \s * ( \{ [ ^ }] * \} )/ )?.[ 1 ];
40 const DEFAULT_COUNTRY = addressForms. match ( /DEFAULT_COUNTRY \s * = \s * '( [A-Z_] + )'/ )?.[ 1 ];
41 if ( ! mapSource || ! DEFAULT_COUNTRY ) throw new Error ( "@wix/headless-forms: ADDRESS_FORM_ID_BY_COUNTRY or DEFAULT_COUNTRY not found in address-forms.js" );
42 const ADDRESS_FORM_ID_BY_COUNTRY = Object. fromEntries ([ ... mapSource. matchAll ( /( [A-Z_] + ): \s * '( [0-9a-f-] {36} )'/ g )]. map (( m ) => [m[ 1 ], m[ 2 ]]));
43
44 // 2 · the template forms, from the same public endpoint Wix's form components fetch at runtime
45 // (@wix/headless-forms services/utils/address-forms.js): all twelve in one call, no token.
46 const formIds = [ ...new Set (Object. values ( ADDRESS_FORM_ID_BY_COUNTRY ))];
47 const url = `https://www.wixapis.com/form-template-service/v1/templates?${ formIds . map (( id ) => `templateIds=${ id }` ). join ( "&" ) }&namespace=wix.form_platform.form` ;
48 const res = await fetch (url);
49 const json = await res. json (). catch (() => ({}));
50 if ( ! res.ok || ! Array. isArray (json.templates)) throw new Error ( `form templates: ${ res . status } ${ JSON . stringify ( json ). slice ( 0 , 200 ) }` );
51 const templates = {};
52 const nameByFormId = {};
53 for ( const id of formIds) {
54 const form = json.templates. find (( t ) => t.id === id);
55 if ( ! form) throw new Error ( `template form ${ id } missing from the response` );
56 const name = String (form.name ?? id). replace ( / ^ MLA_/ , "" );
57 nameByFormId[id] = name;
58 templates[name] = (form.fields ?? []). map (( f ) => ({ sub: f.target, required: Boolean (f.validation?.required), hidden: Boolean (f.hidden) }));
59 }
60 const byCountry = Object. fromEntries (Object. entries ( ADDRESS_FORM_ID_BY_COUNTRY ). filter (([ c ]) => c !== DEFAULT_COUNTRY ). map (([ c , id ]) => [c, nameByFormId[id]]));
61
62 // 3 · country names and subdivisions, in English, from Wix's locale dataset. The names are shipped as
63 // data because Intl.DisplayNames spells a few of them differently in Node and in the browser (Hong
64 // Kong, Macao, Palestine, the Falklands), and a server-rendered list that the browser re-renders
65 // differently is a React hydration mismatch that throws the whole form's server render away.
66 const ld = pack ( "@wix/locale-dataset-data" );
67 const ldVersion = JSON . parse ( readFileSync ( join (ld, "package.json" ), "utf8" )).version;
68 const data = JSON . parse ( readFileSync ( join (ld, "resources" , "data.json" ), "utf8" ));
69 const en = JSON . parse ( readFileSync ( join (ld, "resources" , "translations" , "messages_en.json" ), "utf8" ));
70 const countryNames = {};
71 const subdivisions = {};
72 for ( const c of data.countries) {
73 if (en[c.displayName]) countryNames[c.shortKey] = en[c.displayName]. trim ();
74 if ( ! c.subdivisions?.list?. length ) continue ;
75 subdivisions[c.shortKey] = {
76 label: en[c.subdivisions.displayName] ?? "Region" ,
77 list: c.subdivisions.list. map (( s ) => [s.key, en[s.displayName] ?? s.key]),
78 };
79 }
80
81 const body = `// Generated by templates/forms/tooling/address-templates.mjs on ${ new Date (). toISOString (). slice ( 0 , 10 ) }
82 // from @wix/headless-forms ${ hfVersion } (country → template), the ${ formIds . length } template forms
83 // (wix.form_platform.form, from the public form-template endpoint) and @wix/locale-dataset-data ${ ldVersion }
84 // (country names, subdivisions). Do not edit; re-run the generator.
85 export interface AddressTemplatePart { sub: string; required: boolean; hidden: boolean }
86 export interface AddressSubdivisions { label: string; list: [code: string, name: string][] }
87 export const ADDRESS_TEMPLATES: {
88 defaultTemplate: string;
89 byCountry: Record<string, string>;
90 templates: Record<string, AddressTemplatePart[]>;
91 countryNames: Record<string, string>;
92 subdivisions: Record<string, AddressSubdivisions>;
93 } = ${ JSON . stringify ({ defaultTemplate: nameByFormId[ ADDRESS_FORM_ID_BY_COUNTRY [ DEFAULT_COUNTRY ]] , byCountry , templates , countryNames , subdivisions }) };
94 ` ;
95 writeFileSync ( OUT , body);
96 console. log ( JSON . stringify ({ wrote: OUT , templates: Object. keys (templates), countriesMapped: Object. keys (byCountry). length , countriesNamed: Object. keys (countryNames). length , countriesWithSubdivisions: Object. keys (subdivisions). length , bytes: body. length }));