Skip to content

Commit 9aaa8f5

Browse files
committed
Refactor: Add back in esbuild to cdn deploy
1 parent b927eb2 commit 9aaa8f5

File tree

3 files changed

+280
-5
lines changed

3 files changed

+280
-5
lines changed

.github/workflows/cdn-deploy.yml

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ jobs:
184184
gsub(/LIT_VERSION/, version);
185185
print
186186
}' $GITHUB_WORKSPACE/scripts/assets/templates/cdn/lit-redirect.html.template > cdn/lit/index.html
187-
187+
188188
# Create JavaScript redirect file for importmap compatibility
189189
awk -v version="$LIT_VERSION" '{
190190
gsub(/\${VERSION}/, version);
@@ -225,7 +225,7 @@ jobs:
225225
gsub(/\${PACKAGE_NAME}/, pkgname);
226226
print
227227
}' scripts/helpers/latest-redirect.html.template > "cdn/$dep/index.html"
228-
228+
229229
# Create JavaScript redirect file for importmap compatibility
230230
awk -v version="$DEP_VERSION" '{
231231
gsub(/\${VERSION}/, version);
@@ -279,6 +279,39 @@ jobs:
279279
done
280280
cd ..
281281
282+
283+
# Rewrite imports to use absolute CDN URLs
284+
echo "Rewriting imports for CDN compatibility..."
285+
cp $GITHUB_WORKSPACE/scripts/assets/templates/cdn/esbuild/rewrite-imports.js.template scripts/helpers/rewrite-imports.js
286+
287+
# esbuild and glob are already installed as devDependencies
288+
289+
# Process each package directory to rewrite imports
290+
export CDN_URL="https://cdn.semantic-ui.com"
291+
export VERSION=$VERSION
292+
export LIT_VERSION=$LIT_VERSION
293+
294+
echo "Starting import rewriting process..."
295+
echo "Using: VERSION=$VERSION, LIT_VERSION=$LIT_VERSION"
296+
297+
# Process packages in order
298+
echo "Processing @semantic-ui packages..."
299+
node scripts/helpers/rewrite-imports.js ./cdn/@semantic-ui
300+
301+
echo "Processing lit package..."
302+
node scripts/helpers/rewrite-imports.js ./cdn/lit
303+
304+
# Process Lit dependencies
305+
echo "Processing Lit dependencies..."
306+
for dep in $LIT_DEPS; do
307+
if [ -d "./cdn/$dep" ]; then
308+
echo "Processing $dep..."
309+
node scripts/helpers/rewrite-imports.js "./cdn/$dep"
310+
fi
311+
done
312+
313+
echo "Import rewriting completed."
314+
282315
# Generate production importmap
283316
echo "Generating importmaps for version $VERSION"
284317
@@ -312,7 +345,7 @@ jobs:
312345
gsub(/\${PACKAGE_NAME}/, pkgname);
313346
print
314347
}' scripts/helpers/latest-redirect.html.template > cdn/@semantic-ui/core/index.html
315-
348+
316349
# Create JavaScript redirect file for importmap compatibility
317350
awk -v version="$CORE_VERSION" '{
318351
gsub(/\${VERSION}/, version);
@@ -334,7 +367,7 @@ jobs:
334367
gsub(/\${PACKAGE_NAME}/, pkgname);
335368
print
336369
}' $GITHUB_WORKSPACE/scripts/assets/templates/cdn/latest-redirect.html.template > "../cdn/@semantic-ui/$package_name/index.html"
337-
370+
338371
# Create JavaScript redirect file for importmap compatibility
339372
awk -v version="$pkg_version" '{
340373
gsub(/\${VERSION}/, version);

docs/src/pages/test.astro

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { TestComponent } from '@components/Test/component.js';
99
<script type="importmap">
1010
{
1111
"imports": {
12-
"@semantic-ui/component": "https://cdn.semantic-ui.com/@semantic-ui/component/0.10.6/dist/browser/index.min.js"
12+
"@semantic-ui/component": "https://cdn.semantic-ui.com/@semantic-ui/component/0.10.6-test7/src/index.js"
1313
}
1414
}
1515
</script>
Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
1+
/**
2+
* Script to rewrite imports in package files to use absolute CDN URLs
3+
*
4+
* This allows bare imports like 'import { html } from "lit"' to work
5+
* without needing explicit importmap entries for every nested dependency.
6+
*/
7+
8+
import fs from 'fs';
9+
import path from 'path';
10+
import { fileURLToPath } from 'url';
11+
import { build } from 'esbuild';
12+
import { glob } from 'glob';
13+
14+
const __filename = fileURLToPath(import.meta.url);
15+
const __dirname = path.dirname(__filename);
16+
17+
// Access environment variables
18+
const version = process.env.VERSION;
19+
const cdnUrl = process.env.CDN_URL || 'https://cdn.semantic-ui.com';
20+
21+
// Target directory to process
22+
const argv = process.argv.slice(2);
23+
const targetDir = argv[0];
24+
25+
if (!targetDir) {
26+
console.error('No target directory specified');
27+
process.exit(1);
28+
}
29+
30+
// Validate directories exist
31+
if (!fs.existsSync(targetDir)) {
32+
console.error(`Target directory does not exist: ${targetDir}`);
33+
process.exit(1);
34+
}
35+
36+
// Config
37+
const tmpDir = path.join(process.cwd(), 'tmp-cdn-build');
38+
39+
// Ensure tmp dir exists
40+
if (!fs.existsSync(tmpDir)) {
41+
fs.mkdirSync(tmpDir, { recursive: true });
42+
}
43+
44+
// Get package info from directory path
45+
function getPackageInfo(filePath) {
46+
// Extract package info from path
47+
// Expected format: cdn/@scope/package/version/...
48+
const parts = filePath.split(path.sep);
49+
const cdnIndex = parts.findIndex(part => part === 'cdn');
50+
51+
if (cdnIndex === -1 || parts.length < cdnIndex + 4) {
52+
return null;
53+
}
54+
55+
const scope = parts[cdnIndex + 1].startsWith('@') ? parts[cdnIndex + 1] : null;
56+
const packageName = scope ? parts[cdnIndex + 2] : parts[cdnIndex + 1];
57+
const packageVersion = scope ? parts[cdnIndex + 3] : parts[cdnIndex + 2];
58+
59+
return {
60+
scope,
61+
packageName,
62+
packageVersion,
63+
fullName: scope ? `${scope}/${packageName}` : packageName
64+
};
65+
}
66+
67+
// Extract the root path of a package from a file path
68+
function extractPackageRootPath(filePath, packageInfo) {
69+
// Extract the root package directory path for a given file
70+
// Expected format: cdn/@scope/package/version/...
71+
if (!packageInfo) return null;
72+
73+
const parts = filePath.split(path.sep);
74+
const cdnIndex = parts.findIndex(part => part === 'cdn');
75+
76+
if (cdnIndex === -1) return null;
77+
78+
let packagePathLength;
79+
if (packageInfo.scope) {
80+
// @scope/package/version - need 3 parts after cdn
81+
packagePathLength = cdnIndex + 4;
82+
} else {
83+
// package/version - need 2 parts after cdn
84+
packagePathLength = cdnIndex + 3;
85+
}
86+
87+
if (parts.length < packagePathLength) return null;
88+
89+
return parts.slice(0, packagePathLength).join(path.sep);
90+
}
91+
92+
// Detects if a file is likely ESM or CommonJS
93+
function detectModuleFormat(filePath) {
94+
try {
95+
const content = fs.readFileSync(filePath, 'utf-8');
96+
97+
// Look for ESM indicators
98+
const hasImportExport = /\b(import|export)\b/.test(content);
99+
const hasModuleExports = /\b(module\.exports|exports\.\w+)\b/.test(content);
100+
const hasRequire = /\brequire\s*\(/.test(content);
101+
102+
// If the file has import/export statements, it's likely ESM
103+
if (hasImportExport && !hasModuleExports) {
104+
return 'esm';
105+
}
106+
107+
// If it has module.exports or require, it's likely CommonJS
108+
if ((hasModuleExports || hasRequire) && !hasImportExport) {
109+
return 'commonjs';
110+
}
111+
112+
// If it has both or neither, prefer ESM for .mjs files, otherwise default to ESM
113+
if (filePath.endsWith('.mjs')) {
114+
return 'esm';
115+
}
116+
117+
// Default to ESM for modern packages
118+
return 'esm';
119+
} catch (error) {
120+
console.error(`Error detecting module format for ${filePath}:`, error);
121+
// Default to ESM if detection fails
122+
return 'esm';
123+
}
124+
}
125+
126+
// Rewrite imports in a file
127+
async function rewriteFile(filePath) {
128+
try {
129+
// Skip non-JS files
130+
if (!filePath.endsWith('.js') && !filePath.endsWith('.mjs')) {
131+
return;
132+
}
133+
134+
// Get package info from file path
135+
const packageInfo = getPackageInfo(filePath);
136+
if (!packageInfo) {
137+
console.warn(`Could not determine package info for ${filePath}`);
138+
return;
139+
}
140+
141+
// Detect module format
142+
const format = detectModuleFormat(filePath);
143+
console.log(`Detected ${format} format for ${path.basename(filePath)}`);
144+
145+
// Generate output file path (preserving directory structure)
146+
const relativePath = path.relative(targetDir, filePath);
147+
const outputPath = path.join(tmpDir, relativePath);
148+
149+
// Ensure output directory exists
150+
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
151+
152+
console.log(`Setting up esbuild for file: ${filePath}`);
153+
console.log(`Output path: ${outputPath}`);
154+
console.log(`Module format: ${format}`);
155+
156+
// Use esbuild to transform the file
157+
await build({
158+
entryPoints: [filePath],
159+
outfile: outputPath,
160+
bundle: false,
161+
format: format, // Use the detected format (esm or commonjs)
162+
write: true,
163+
logLevel: 'info', // Increase log level for debugging
164+
logLevel: 'warning' // Reduce noise in logs
165+
});
166+
167+
console.log(`Processed: ${filePath} -> ${outputPath}`);
168+
169+
// Copy the output back to the original location
170+
fs.copyFileSync(outputPath, filePath);
171+
172+
} catch (error) {
173+
console.error(`Error processing ${filePath}:`, error);
174+
}
175+
}
176+
177+
// Process all JS files in the target directory
178+
async function processDirectory() {
179+
try {
180+
console.log(`Starting processDirectory with target: ${targetDir}`);
181+
182+
// Ensure the directory is absolute
183+
const absoluteTargetDir = path.resolve(targetDir);
184+
console.log(`Absolute target directory: ${absoluteTargetDir}`);
185+
186+
// Check if directory exists
187+
if (!fs.existsSync(absoluteTargetDir)) {
188+
console.error(`ERROR: Target directory does not exist: ${absoluteTargetDir}`);
189+
return;
190+
}
191+
192+
console.log(`Looking for JS files in: ${absoluteTargetDir}`);
193+
194+
// Find all JS files (ensure we're handling .mjs files too)
195+
const jsFiles = glob.sync('**/*.{js,mjs}', {
196+
cwd: absoluteTargetDir,
197+
absolute: true
198+
});
199+
200+
console.log(`Glob pattern used: **/*.{js,mjs}`);
201+
console.log(`Files found by glob: ${jsFiles.length}`);
202+
203+
console.log(`Found ${jsFiles.length} JS files to process in ${absoluteTargetDir}`);
204+
205+
if (jsFiles.length === 0) {
206+
console.warn(`No JS files found in ${absoluteTargetDir}. Check if the directory is correct.`);
207+
return; // Exit early if no files found
208+
}
209+
210+
// Log the first few files to help with debugging
211+
console.log("Sample files to process:");
212+
jsFiles.slice(0, 3).forEach(file => console.log(` - ${file}`));
213+
214+
// Process files in parallel with a concurrency limit
215+
const concurrency = 10;
216+
let processedCount = 0;
217+
218+
for (let i = 0; i < jsFiles.length; i += concurrency) {
219+
const batch = jsFiles.slice(i, i + concurrency);
220+
await Promise.all(batch.map(async file => {
221+
await rewriteFile(file);
222+
processedCount++;
223+
}));
224+
225+
// Log progress for large directories
226+
if (jsFiles.length > 20 && (i + concurrency) % 50 === 0) {
227+
console.log(`Processed ${processedCount}/${jsFiles.length} files...`);
228+
}
229+
}
230+
231+
console.log(`Import rewriting completed. Processed ${processedCount} files.`);
232+
} catch (error) {
233+
console.error('Error processing directory:', error);
234+
process.exit(1);
235+
} finally {
236+
// Clean up tmp directory
237+
fs.rmSync(tmpDir, { recursive: true, force: true });
238+
}
239+
}
240+
241+
// Run the script
242+
processDirectory();

0 commit comments

Comments
 (0)