Setting the file. One moment.
Generated Plugin Handoff · Wix Replatform · wix/skills · Skills Docs
ContentsBack to the top of the page — line 71
This file
Number 45.84
Position 84 of 182
Type JavaScript
Size 9 KB
Lines 143 lib/ generated-plugin-handoff.js
JavaScript · 143 lines · 9 KB
9 const FILES = [ 'src/import/plugin-handoff.js' , 'src/import/run-import.js' , 'src/extract/run-extract.js' ];
10 const HELPERS = [ 'plugin-entity-read-plan.js' , 'structure-bridge-reader.js' , 'wix-wp-plugin-v2-client.js' ];
11 const digest = ( bytes ) => crypto. createHash ( 'sha256' ). update (bytes). digest ( 'hex' );
12 function canonicalEntities ( projectDir ) {
13 const read = ( name ) => fs. existsSync (path. join (projectDir, name)) ? JSON . parse (fs. readFileSync (path. join (projectDir, name), 'utf8' )) : {};
14 const schema = read ( 'source-schema.json' );
15 const mapping = read ( 'mapping/mapping-plan.json' );
16 const rows = ( value ) => Array. isArray (value) ? value : Object. entries (value || {}). filter (([, row ]) => row && typeof row === 'object' ). map (([ key , row ]) => ({ sourceEntityRef: key, ... row }));
17 const sources = rows (schema.entities);
18 const decisions = require ( './website-handoff.js' ). readEntityMappings (mapping);
19 const referenceOf = ( row ) => row.sourceEntityRef || row.ref || row.entity || row.sourceEntity || row.entityKey || ( typeof row.source === 'string' ? row.source : null );
20 const planned = new Set (decisions. filter (( row ) => row.targetRef && ! [ 'excluded' , 'declined' , 'pending' , 'deferred' , 'manual' , 'blocked' , 'unsupported' ]. includes (row.status))
21 . map (( row ) => referenceOf (row)));
22 const combined = new Map (sources. map (( row ) => [ referenceOf (row), row]));
23 for ( const decision of decisions) {
24 const key = referenceOf (decision);
25 if ( ! key) throw new Error ( 'mapping entity lacks identity' );
26 const source = combined. get (key) || {};
27 combined. set (key, { ... source, ... decision, sourceMeta: { ... decision.sourceMeta, ... source.sourceMeta } });
28 }
29 return [ ... combined]. filter (([ key ]) => key). map (([ key , row ]) => ({
30 sourceEntityRef: key, sourceEntity: row.sourceEntity || row.entityKey || row.entity || ( typeof row.source === 'string' ? row.source : key),
31 sourceType: row.sourceType || null ,
32 sourceTypes: row.sourceTypes || null ,
33 bridgeDesignated: Boolean (row.bridgeDesignated || row.sourceMeta?.extractionPlan || row.fulfillment?.kind === 'structure-bridge-plugin' ),
34 sourceMeta: row.sourceMeta?.extractionPlan ? { extractionPlan: row.sourceMeta.extractionPlan } : {},
35 sourcePrerequisites: row.sourcePrerequisites || [],
36 importPlanned: planned. has (key),
37 })). sort (( a , b ) => a.sourceEntityRef. localeCompare (b.sourceEntityRef));
38 }
39 function templates ( entities ) {
40 const config = JSON . stringify ({ version: VERSION , entities }, null , 2 );
41 const wiring = `'use strict'; \n const runtime = require('../lib/plugin-entity-read-plan.js'); \n const config = ${ config }; \n module.exports = { config, run: (kind, projectDir, args) => runtime.runGeneratedPluginEntrypoint({ kind, projectDir, entities: config.entities }, args) }; \n ` ;
42 const entry = ( kind , directory ) => `'use strict'; \n const path = require('node:path'); \n const handoff = require('${ directory }plugin-handoff.js'); \n handoff.run('${ kind }', path.resolve(__dirname, '../..'), process.argv.slice(2)).catch((error) => { console.error(error.message); process.exitCode = Number.isInteger(error.exitCode) ? error.exitCode : 1; }); \n ` ;
43 return { [ FILES [ 0 ]]: wiring, [ FILES [ 1 ]]: entry ( 'import' , './' ), [ FILES [ 2 ]]: entry ( 'extract' , '../import/' ) };
44 }
45 function moduleSourceDir () { return path. resolve (__dirname, '../resources/rp-source-wordpress/lib' ); }
46 function vendorSharedDependencies ( projectDir ) {
47 const visited = new Set ();
48 function copy ( file ) {
49 if (visited. has (file)) return ;
50 visited. add (file);
51 const source = fs. readFileSync (file, 'utf8' );
52 const destination = noSymlinks (projectDir, `src/lib/${ path . basename ( file ) }` );
53 fs. mkdirSync (path. dirname (destination), { recursive: true });
54 fs. copyFileSync (file, destination);
55 for ( const match of source. matchAll ( /require \( ['"] ( . \/ [ ^ '"] + ) ['"] \) / g )) copy (require. resolve (path. resolve (path. dirname (file), match[ 1 ])));
56 }
57 copy (path. join (__dirname, 'import-recovery.js' ));
58 copy (path. join (__dirname, 'import-readiness-contract.js' ));
59 // The entrypoint's stop handling and the wix_auth_expired exit code.
60 copy (path. join (__dirname, 'import-interruption.js' ));
61 copy (path. join (__dirname, 'wix-auth.js' ));
62 }
63 function noSymlinks ( projectDir , relativePath ) {
64 let cursor = path. resolve (projectDir);
65 for ( const token of relativePath. split ( '/' )) {
66 cursor = path. join (cursor, token);
67 if (fs. existsSync (cursor) && fs. lstatSync (cursor). isSymbolicLink ()) throw new Error ( 'generated handoff symlink refused' );
68 }
69 return cursor;
70 }
71 function validateDispatchMapping ( projectDir ) {
72 const file = path. join (projectDir, 'mapping/mapping-plan.json' );
73 const mapping = fs. existsSync (file) ? JSON . parse (fs. readFileSync (file, 'utf8' )) : {};
74 const rows = require ( './website-handoff.js' ). readEntityMappings (mapping);
75 for ( const row of rows) {
76 if ( ! row.targetRef && ! [ 'excluded' , 'declined' , 'pending' , 'deferred' , 'manual' , 'blocked' , 'unsupported' ]. includes (row.status)) {
77 throw new Error ( 'mapping targetRef is missing; normalize the declared legacy target before emitting dispatch, never drop its scope' );
78 }
79 }
80 }
81 function emitPluginHandoff ( projectDir ) {
82 const entities = canonicalEntities (projectDir);
83 validateDispatchMapping (projectDir);
84 if ( ! entities. some (( entity ) => entity.bridgeDesignated)) throw new Error ( 'no canonical bridge entities' );
85 const emitted = templates (entities);
86 // Inspect every destination before any mutation. Existing project entrypoints must be
87 // deliberately adapted, never silently replaced by adding one bridge entity.
88 for ( const [ name , expected ] of Object. entries (emitted)) {
89 const file = noSymlinks (projectDir, name);
90 if ( ! fs. existsSync (file)) continue ;
91 const existing = fs. readFileSync (file, 'utf8' );
92 let owned = existing === expected;
93 if ( ! owned && name === FILES [ 0 ]) {
94 try {
95 const match = existing. match ( /const config = ( [\s\S] *? ); \n module . exports =/ );
96 const config = JSON . parse (match[ 1 ]);
97 owned = config.version === VERSION && existing === templates (config.entities)[name];
98 } catch { /* Unrecognized wiring belongs to the project. */ }
99 }
100 if ( ! owned) throw new Error ( `refusing to overwrite non-template entrypoint: ${ name }; preserve its ordering, self-check, heartbeat and ledger in the project adapter before deliberately replacing it` );
101 }
102 for ( const [ name , text ] of Object. entries (emitted)) {
103 const file = noSymlinks (projectDir, name);
104 fs. mkdirSync (path. dirname (file), { recursive: true });
105 fs. writeFileSync (file, text);
106 }
107 for ( const helper of HELPERS ) {
108 const file = noSymlinks (projectDir, `src/lib/${ helper }` );
109 fs. mkdirSync (path. dirname (file), { recursive: true });
110 fs. copyFileSync (path. join ( moduleSourceDir (), helper), file);
111 }
112 vendorSharedDependencies (projectDir);
113 return { version: VERSION , files: FILES };
114 }
115 function verifyPluginHandoff ( projectDir , selection = {}) {
116 const entities = canonicalEntities (projectDir);
117 const selected = require (path. join ( moduleSourceDir (), 'plugin-entity-read-plan.js' )). selectPluginEntities (entities, selection);
118 const byRef = new Map (entities. map (( entity ) => [entity.sourceEntityRef, entity]));
119 const closure = new Map ();
120 const visit = ( entity , stack = new Set ()) => {
121 if (stack. has (entity.sourceEntityRef)) throw new Error ( 'cyclic source prerequisites' );
122 if (closure. has (entity.sourceEntityRef)) return ;
123 closure. set (entity.sourceEntityRef, entity);
124 for ( const ref of entity.sourcePrerequisites) {
125 const source = byRef. get (ref);
126 if ( ! source) throw new Error ( 'missing canonical source prerequisite' );
127 visit (source, new Set ([ ... stack, entity.sourceEntityRef]));
128 }
129 };
130 selected. forEach (( entity ) => visit (entity));
131 if ( ! [ ... closure. values ()]. some (( entity ) => entity.bridgeDesignated)) return { applicable: false , entities: [] };
132 validateDispatchMapping (projectDir);
133 for ( const [ name , expected ] of Object. entries ( templates (entities))) {
134 const file = noSymlinks (projectDir, name);
135 if ( ! fs. existsSync (file) || fs. readFileSync (file, 'utf8' ) !== expected) throw new Error ( `generated plugin handoff differs from committed template: ${ name }` );
136 }
137 for ( const helper of HELPERS ) {
138 const file = noSymlinks (projectDir, `src/lib/${ helper }` );
139 if ( ! fs. existsSync (file) || digest (fs. readFileSync (file)) !== digest (fs. readFileSync (path. join ( moduleSourceDir (), helper)))) throw new Error ( `changed vendored plugin helper: ${ helper }` );
140 }
141 return { applicable: true , entities: [ ... closure. values ()] };
142 }
143 module . exports = { VERSION, FILES, HELPERS, canonicalEntities, emitPluginHandoff, verifyPluginHandoff, templates };