()
| 115 | |
| 116 | const installedVersion = fs.readFileSync(versionFile, 'utf8').trim(); |
| 117 | if (installedVersion !== pluginVersion) return true; |
| 118 | |
| 119 | const pluginScripts = path.join(PLUGIN_ROOT, 'scripts'); |
| 120 | const projectScripts = path.join(PROJECT_ROOT, '.citadel', 'scripts'); |
| 121 | if (!fs.existsSync(pluginScripts) || !fs.existsSync(projectScripts)) return true; |
| 122 | const moduleScope = path.join(projectScripts, 'package.json'); |
| 123 | if (!fs.existsSync(moduleScope)) return true; |
| 124 | try { |
| 125 | if (JSON.parse(fs.readFileSync(moduleScope, 'utf8')).type !== 'commonjs') return true; |
| 126 | } catch { |
| 127 | return true; |
| 128 | } |
| 129 | return availableDelegates(pluginScripts) |
| 130 | .some((file) => !fs.existsSync(path.join(projectScripts, file))); |
| 131 | } catch { |
| 132 | return true; // on any error, sync to be safe |
| 133 | } |
| 134 | } |
| 135 | |
| 136 | function writeVersionFile(pluginRoot, projectRoot) { |
| 137 | try { |
| 138 | const pkgPath = path.join(pluginRoot, 'package.json'); |
| 139 | if (!fs.existsSync(pkgPath)) return; |
| 140 | const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8')); |
| 141 | fs.writeFileSync(path.join(projectRoot, '.citadel', 'version.txt'), `${pkg.version || '0.0.0'}\n`); |
| 142 | } catch { /* non-fatal */ } |
| 143 | } |
| 144 | |
| 145 | function ensureDir(dir) { |
| 146 | if (!fs.existsSync(dir)) { |
| 147 | fs.mkdirSync(dir, { recursive: true }); |
| 148 | } |
| 149 | } |
| 150 | |
| 151 | function copyDirRecursive(src, dest) { |
| 152 | ensureDir(dest); |
| 153 | for (const entry of fs.readdirSync(src, { withFileTypes: true })) { |
| 154 | const srcPath = path.join(src, entry.name); |
| 155 | const destPath = path.join(dest, entry.name); |
| 156 | if (entry.isDirectory()) { |
| 157 | copyDirRecursive(srcPath, destPath); |
| 158 | } else { |
| 159 | fs.copyFileSync(srcPath, destPath); |
| 160 | } |
| 161 | } |
| 162 | } |
| 163 | |
| 164 | /** |
| 165 | * Copy files from src to dest, but only if they don't already exist. |
| 166 | * Preserves user customizations to templates. |
| 167 | */ |
| 168 | function copyDirIfMissing(src, dest) { |
| 169 | ensureDir(dest); |
| 170 | for (const entry of fs.readdirSync(src, { withFileTypes: true })) { |
| 171 | const srcPath = path.join(src, entry.name); |
| 172 | const destPath = path.join(dest, entry.name); |
| 173 | if (entry.isDirectory()) { |
| 174 | copyDirIfMissing(srcPath, destPath); |
no test coverage detected