clone of github.com/decent-newsroom/newsroom
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

293 lines
10 KiB

#!/usr/bin/env php
<?php
/**
* Copy packages from node_modules to assets/vendor and update importmap.php
* This script replaces Symfony's importmap:install by using npm packages
*/
$baseDir = __DIR__ . '/..';
$importmapFile = $baseDir . '/importmap.php';
$nodeModulesDir = $baseDir . '/node_modules';
$assetsVendorDir = $baseDir . '/assets/vendor';
if (!file_exists($importmapFile)) {
fwrite(STDERR, "Error: importmap.php not found\n");
exit(1);
}
if (!is_dir($nodeModulesDir)) {
fwrite(STDERR, "Error: node_modules directory not found. Run 'npm install' first.\n");
exit(1);
}
// Create assets/vendor directory
if (!is_dir($assetsVendorDir)) {
mkdir($assetsVendorDir, 0755, true);
}
$map = require $importmapFile;
$updated = false;
$errors = [];
/**
* Find the entry point file for a package
*/
function findEntryPoint($packageDir) {
if (!is_dir($packageDir)) {
return null;
}
$packageJson = $packageDir . '/package.json';
if (file_exists($packageJson)) {
$pkg = json_decode(file_get_contents($packageJson), true);
// Check exports field (ESM)
if (isset($pkg['exports'])) {
$exports = $pkg['exports'];
if (is_string($exports)) {
$exports = ['.' => $exports];
}
if (isset($exports['.']['import']) || isset($exports['.']['default'])) {
$exportPath = $exports['.']['import'] ?? $exports['.']['default'];
if ($exportPath && file_exists($packageDir . '/' . $exportPath)) {
return $packageDir . '/' . $exportPath;
}
}
}
// Check module field (ESM)
if (isset($pkg['module']) && file_exists($packageDir . '/' . $pkg['module'])) {
return $packageDir . '/' . $pkg['module'];
}
// Check main field
if (isset($pkg['main']) && file_exists($packageDir . '/' . $pkg['main'])) {
return $packageDir . '/' . $pkg['main'];
}
}
// Fallback: try common locations
$commonPaths = [
'/dist/index.js',
'/index.js',
'/esm/index.js',
'/es/index.js',
'/lib/index.js',
'/src/index.js',
'/main.js',
];
foreach ($commonPaths as $path) {
if (file_exists($packageDir . $path)) {
return $packageDir . $path;
}
}
// Last resort: find any .js file in root
$files = glob($packageDir . '/*.js');
if (!empty($files)) {
return $files[0];
}
// Try dist directory
$distFiles = glob($packageDir . '/dist/*.js');
if (!empty($distFiles)) {
return $distFiles[0];
}
return null;
}
/**
* Copy package to assets/vendor
*/
function copyPackage($packageName, $nodeModulesDir, $assetsVendorDir) {
// Handle scoped packages
$parts = explode('/', $packageName);
if ($packageName[0] === '@') {
$sourceDir = $nodeModulesDir . '/' . $parts[0] . '/' . $parts[1];
$targetDir = $assetsVendorDir . '/' . $parts[0] . '/' . $parts[1];
} else {
$sourceDir = $nodeModulesDir . '/' . $parts[0];
$targetDir = $assetsVendorDir . '/' . $parts[0];
}
if (!is_dir($sourceDir)) {
return ['success' => false, 'error' => "Package not found in node_modules: $packageName"];
}
$entryPoint = findEntryPoint($sourceDir);
if (!$entryPoint) {
return ['success' => false, 'error' => "Could not find entry point for: $packageName"];
}
// Create target directory
if (!is_dir($targetDir)) {
mkdir($targetDir, 0755, true);
}
// Copy entry point to index.js
$targetFile = $targetDir . '/index.js';
if (!copy($entryPoint, $targetFile)) {
return ['success' => false, 'error' => "Failed to copy file: $entryPoint"];
}
// Copy package.json for reference (optional)
$pkgJson = $sourceDir . '/package.json';
if (file_exists($pkgJson)) {
copy($pkgJson, $targetDir . '/package.json');
}
return ['success' => true, 'path' => './assets/vendor/' . str_replace($assetsVendorDir . '/', '', $targetFile)];
}
// Process each entry in importmap
foreach ($map as $name => &$config) {
// Only process entries with version but no path (need to be installed)
if (isset($config['version']) && !isset($config['path'])) {
// Extract package name (handle subpaths like 'quill/dist/quill.core.css')
$parts = explode('/', $name);
if ($name[0] === '@') {
// Scoped package: @noble/curves/secp256k1 -> @noble/curves
$packageName = $parts[0] . '/' . $parts[1];
} else {
// Regular package: quill/dist/quill.core.css -> quill
$packageName = $parts[0];
}
$result = copyPackage($packageName, $nodeModulesDir, $assetsVendorDir);
if ($result['success']) {
// Update importmap.php entry - remove version when adding path
$config['path'] = $result['path'];
unset($config['version']); // Remove version when path is set
$updated = true;
echo "✓ Installed: $name -> {$result['path']}\n";
} else {
$errors[] = "$name: {$result['error']}";
echo "✗ Failed: $name - {$result['error']}\n";
}
}
}
// Handle CSS files and subpaths - copy the full package structure
foreach ($map as $name => &$config) {
if (isset($config['version']) && isset($config['type']) && $config['type'] === 'css') {
// For CSS files, we need to copy from node_modules/dist
$parts = explode('/', $name);
$packageName = $parts[0];
$subPath = implode('/', array_slice($parts, 1));
if ($packageName[0] === '@') {
$sourceFile = $nodeModulesDir . '/' . $parts[0] . '/' . $parts[1] . '/' . implode('/', array_slice($parts, 2));
$targetDir = $assetsVendorDir . '/' . $parts[0] . '/' . $parts[1];
} else {
$sourceFile = $nodeModulesDir . '/' . $packageName . '/' . $subPath;
$targetDir = $assetsVendorDir . '/' . $packageName;
}
if (file_exists($sourceFile)) {
$targetFile = $targetDir . '/' . $subPath;
$targetFileDir = dirname($targetFile);
if (!is_dir($targetFileDir)) {
mkdir($targetFileDir, 0755, true);
}
copy($sourceFile, $targetFile);
$config['path'] = './assets/vendor/' . str_replace($assetsVendorDir . '/', '', $targetFile);
$updated = true;
echo "✓ Installed CSS: $name -> {$config['path']}\n";
}
}
}
// Handle subpaths (like 'nostr-tools/nip46')
foreach ($map as $name => &$config) {
if (isset($config['version']) && !isset($config['path']) && strpos($name, '/') !== false && !isset($config['type'])) {
$parts = explode('/', $name);
$packageName = $parts[0];
$subPath = $parts[1];
if ($packageName[0] === '@') {
$sourceFile = $nodeModulesDir . '/' . $parts[0] . '/' . $parts[1] . '/' . $subPath . '.js';
$targetDir = $assetsVendorDir . '/' . $parts[0] . '/' . $parts[1];
} else {
$sourceFile = $nodeModulesDir . '/' . $packageName . '/' . $subPath . '.js';
$targetDir = $assetsVendorDir . '/' . $packageName;
}
// Try .mjs extension too
if (!file_exists($sourceFile)) {
$sourceFile = str_replace('.js', '.mjs', $sourceFile);
}
if (file_exists($sourceFile)) {
$targetFile = $targetDir . '/' . $subPath . '.js';
$targetFileDir = dirname($targetFile);
if (!is_dir($targetFileDir)) {
mkdir($targetFileDir, 0755, true);
}
copy($sourceFile, $targetFile);
$config['path'] = './assets/vendor/' . str_replace($assetsVendorDir . '/', '', $targetFile);
unset($config['version']); // Remove version when path is set
$updated = true;
echo "✓ Installed subpath: $name -> {$config['path']}\n";
}
}
}
// Write updated importmap.php
if ($updated) {
$content = "<?php\n\n";
$content .= "/**\n";
$content .= " * Returns the importmap for this application.\n";
$content .= " *\n";
$content .= " * - \"path\" is a path inside the asset mapper system. Use the\n";
$content .= " * \"debug:asset-map\" command to see the full list of paths.\n";
$content .= " *\n";
$content .= " * - \"entrypoint\" (JavaScript only) set to true for any module that will\n";
$content .= " * be used as an \"entrypoint\" (and passed to the importmap() Twig function).\n";
$content .= " *\n";
$content .= " * The \"importmap:require\" command can be used to add new entries to this file.\n";
$content .= " *\n";
$content .= " * This file is auto-generated from npm packages. Run scripts/npm-to-importmap.php to update.\n";
$content .= " */\n";
$content .= "return [\n";
foreach ($map as $name => $config) {
$nameEscaped = str_replace("'", "\\'", $name);
$content .= " '$nameEscaped' => [\n";
if (isset($config['path'])) {
$pathEscaped = str_replace("'", "\\'", $config['path']);
$content .= " 'path' => '$pathEscaped',\n";
}
if (isset($config['version'])) {
$versionEscaped = str_replace("'", "\\'", $config['version']);
$content .= " 'version' => '$versionEscaped',\n";
}
if (isset($config['type'])) {
$typeEscaped = str_replace("'", "\\'", $config['type']);
$content .= " 'type' => '$typeEscaped',\n";
}
if (isset($config['entrypoint'])) {
$content .= " 'entrypoint' => " . ($config['entrypoint'] ? 'true' : 'false') . ",\n";
}
$content .= " ],\n";
}
$content .= "];\n";
file_put_contents($importmapFile, $content);
echo "\n✓ Updated importmap.php\n";
}
if (!empty($errors)) {
echo "\n⚠ Errors encountered:\n";
foreach ($errors as $error) {
echo " - $error\n";
}
exit(1);
}
echo "\n✓ All packages installed successfully!\n";