feat(CLI Tool): Return flexible codebase files to local environment on canvas pull

AI Disclosure: Yes. Used Claude to help develop and document this change.

Closes #3591862 (closed)

Summary

Extends the Canvas CLI push/pull to include the flexible codebase files a project needs to be reconstructed on disk. On push, in addition to component code, the CLI now stores and later restores:

  • package.json — pushed to and stored on the asset library, restored on pull. A terminal note prompts running npm install when it changes.
  • Local helper modules a component imports via the @/… alias — overwritten on pull.
  • Binary assets (images, video, SVG) imported via @/…, ./, or ../ — overwritten on pull.
  • Transitively- or relatively-imported local modules stay bundled into the existing runtime entry rather than emitted as standalone artifacts. Their verbatim source is preserved separately so a pull can still restore the editable file on disk.

Global asset library changes

assets entries gain new fields:

  • path — the original relative disk path, so pull writes each file back to its true location. Derived from the resolved absolute path, never from the synthetic @/… specifier.
  • source — the verbatim original source of a local module import.
  • url — where the CLI can download the file over HTTP (external, computed fresh each response). Text modules skip url: their content rides inline in source; only binaries need fetching.

New top-level keys:

  • packageJson — the project package.json content as a string.
  • bundledSources — verbatim sources of local modules that were bundled into another artifact and so have no standalone runtime entry (transitive or relative imports). Each entry is { path, source }, keyed by disk path. These carry no uri and are never part of the import map; they exist only so a pull can reconstruct the editable file.
  "assets": [                                                                                                                                                                                                    
    {                                                                                                                                                                                                            
      "name": "@/assets/mountain.png",                                                                                                                                                                           
      "uri": "public://canvas/assets/mountain-COB9CijR.png",                                                                                                                                                     
      "path": "src/assets/mountain.png",                                                                                                                                                                         
      "url": "/sites/default/files/canvas/assets/mountain-COB9CijR.png"                                                                                                                                          
    },                                                                                                                                                                                                           
    {                                                                                                                                                                                                            
      "name": "@/lib/formatTitle",                                                                                                                                                                               
      "uri": "public://canvas/assets/formatTitle-CYObcZwM.js",                                                                                                                                                   
      "path": "src/lib/formatTitle.ts",                                                                                                                                                                          
      "source": "export function formatTitle(value: string): string {\n  return value.trim();\n}\n",                                                                                                             
      "url": "/sites/default/files/canvas/assets/formatTitle-CYObcZwM.js"                                                                                                                                        
    }                                                                                                                                                                                                            
  ],                                                                                                                                                                                                             
  "bundledSources": [                                                                                                                                                                                            
    { "path": "src/lib/relative-helper.ts", "source": "export const x = 1;\n" }                                                                                                                                  
  ],                                                                                                                                                                                                             
  "packageJson": "…package.json content as string…"                                                                                                                                                              

canvas-manifest.json changes

Two new top-level keys carry the metadata needed to reconstruct files on disk. Everything else (vendor, local, shared) is unchanged and still acts as the import map.

  • localSources — per-entry { path, source? } for local artifacts in the import map. source is present for text modules, absent for binaries.
  • bundledSources — array of { path, source } for local modules bundled into other artifacts (no standalone artifact, no import-map entry).
{                                                                                                                                                                                                              
  "vendor": { "lodash": "./vendor/lodash-abc123.js" },                                                                                                                                                         
  "local": {                                                                                                                                                                                                   
    "@/lib/foo": "./local/foo-abc.js",                                                                                                                                                                         
    "@/assets/poster.webp": "./local/poster-def.webp"                                                                                                                                                          
  },                                                                                                                                                                                                           
  "localSources": {                                                                                                                                                                                            
    "@/assets/poster.webp": { "path": "src/assets/poster.webp" },                                                                                                                                              
    "@/lib/foo": { "path": "src/lib/foo.ts", "source": "export const x = 1;\n" }                                                                                                                               
  },                                                                                                                                                                                                           
  "bundledSources": [                                                                                                                                                                                          
    { "path": "src/lib/relative-helper.ts", "source": "export const y = 2;\n" }                                                                                                                                
  ],                                                                                                                                                                                                           
  "shared": []                                                                                                                                                                                                 
}                                                                                                                                                                                                              

Testing instructions

  • Run canvas push on a project with local modules and imported assets.
  • Delete or alter a local module, a transitively-imported helper, or package.json, then run canvas pull and confirm each is restored to its original path.
  • Run canvas pull from an empty repo and confirm all flexible codebase files are returned.
  • Confirm the terminal note about running npm install appears when package.json changed.
Edited by Harumi Jang

Merge request reports

Loading