svelte-bundle 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ReadMe.md +38 -0
- package/bundle.js +119 -0
- package/cli.js +108 -0
- package/package.json +39 -0
package/ReadMe.md
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Svelte Bundle CLI
|
|
2
|
+
|
|
3
|
+
**Svelte Bundle CLI** is a simple command-line tool that allows you to bundle a Svelte application into a single `.html` file using Vite and SSR (Server-Side Rendering). The goal of this tool is to make it easy to bundle Svelte apps for deployment, particularly for cases where everything needs to be contained in a single file.
|
|
4
|
+
|
|
5
|
+
Just note, the purpose of this tool is **NOT** to bundle an entire Svelte app, in-fact it is highly discouraged due to the overhead of the generated file. The purpose of this is to expand the capabilities of svelte to work on systems like certain Content Management Systems (CMS) that only allow HTML, CSS, and JS. It also was created with SSR so that the generated file is SEO-safe where necessary elements are already hydrated.
|
|
6
|
+
|
|
7
|
+
Utilizing this you will be able to develop a page with the joy of svelte and be able to directly get a single `.html` file that you can utilize getting full use-case of svelte.
|
|
8
|
+
|
|
9
|
+
⚠️ **Note**: This tool is **NOT** made to function with SvelteKit, this takes a single `.svelte` file as input and utilizes vite to compile it into a single `.html` file.
|
|
10
|
+
|
|
11
|
+
⚠️ **Note**: This tool is currently in early development and is not fully complete. The roadmap and additional features will be added over time. There is currently **NO** testing on this, meaning there is no guarentee it will work for most usecases.
|
|
12
|
+
|
|
13
|
+
## Inspiration
|
|
14
|
+
This tool was inspired by the need I had when it came to updating the CMS for a company I worked for. They were looking for more custom content on their website which used an outdated CMS. Pages were only able to include HTML, CSS, and JS.
|
|
15
|
+
|
|
16
|
+
Pure HTML, CSS, and JS can be grainular and more-importantly, lacks the reactivity that Svelte has. Meaning, to develop certain features I had to focus a lot more on the "what to do" when data changes, rather than Svelte handling that for me.
|
|
17
|
+
|
|
18
|
+
So, I searched for tools around that could be of assistance. I found [figsvelte](https://github.com/thomas-lowry/figsvelte) which was of so much help in the underlying understanding of what to do. But, it did not accomplish all I was looking for. I needed a solution that didn't just generate an HTML file with JS that hydrated the page. Through a lot of tinkering around I was able to finally get the system to work.
|
|
19
|
+
|
|
20
|
+
I noticed through a lot of google searches I wasn't the only one looking for a solution like this, yet, I was unable to find one that addressed everything I was looking for. So, for this reason I have decided to build svelte-bundle to take care of this in a much more simplistic and CLI way.
|
|
21
|
+
|
|
22
|
+
## Features (In Progress)
|
|
23
|
+
- [ ] Bundles Svelte applications using Vite and SSR.
|
|
24
|
+
- [ ] Outputs a single `.html` file ready for deployment.
|
|
25
|
+
- [ ] CLI arguments for specifying input and output directories.
|
|
26
|
+
|
|
27
|
+
## Roadmap
|
|
28
|
+
- [ ] Add options for SSR hydration
|
|
29
|
+
- [ ] Handle CSS and assets within the bundled file.
|
|
30
|
+
- [ ] Implement error handling and more robust validation.
|
|
31
|
+
- [ ] Add support for environment-specific builds (development/production).
|
|
32
|
+
- [ ] Documentation and guides on using the tool with different Svelte apps.
|
|
33
|
+
- [ ] Tests and CI integration.
|
|
34
|
+
|
|
35
|
+
## Installation (Planned)
|
|
36
|
+
Once the tool is published on npm, it will be available via `npx`:
|
|
37
|
+
```bash
|
|
38
|
+
npx svelte-bundle -i <input-dir> -o <output-dir>
|
package/bundle.js
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import fs from 'fs/promises';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { rollup } from 'rollup';
|
|
4
|
+
import svelte from 'rollup-plugin-svelte';
|
|
5
|
+
import resolve from '@rollup/plugin-node-resolve';
|
|
6
|
+
import commonjs from '@rollup/plugin-commonjs';
|
|
7
|
+
import { compile } from 'svelte/compiler';
|
|
8
|
+
import css from 'rollup-plugin-css-only';
|
|
9
|
+
import terser from '@rollup/plugin-terser';
|
|
10
|
+
|
|
11
|
+
export async function buildStaticFile(svelteFilePath, outputDir) {
|
|
12
|
+
try {
|
|
13
|
+
let cssText = '';
|
|
14
|
+
|
|
15
|
+
// Create temporary SSR bundle
|
|
16
|
+
const ssrBundle = await rollup({
|
|
17
|
+
input: svelteFilePath,
|
|
18
|
+
plugins: [
|
|
19
|
+
svelte({
|
|
20
|
+
compilerOptions: {
|
|
21
|
+
generate: 'ssr',
|
|
22
|
+
hydratable: true,
|
|
23
|
+
css: false
|
|
24
|
+
},
|
|
25
|
+
emitCss: true
|
|
26
|
+
}),
|
|
27
|
+
css({
|
|
28
|
+
output: function(styles) {
|
|
29
|
+
cssText = styles;
|
|
30
|
+
}
|
|
31
|
+
}),
|
|
32
|
+
resolve({
|
|
33
|
+
browser: true,
|
|
34
|
+
dedupe: ['svelte']
|
|
35
|
+
}),
|
|
36
|
+
commonjs()
|
|
37
|
+
],
|
|
38
|
+
external: ['svelte/internal']
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
// Create a temporary directory for SSR
|
|
42
|
+
const tempDir = path.join(path.dirname(svelteFilePath), '.temp');
|
|
43
|
+
await fs.mkdir(tempDir, { recursive: true });
|
|
44
|
+
const tempSSRFile = path.join(tempDir, 'ssr-bundle.js');
|
|
45
|
+
|
|
46
|
+
// Generate SSR bundle
|
|
47
|
+
await ssrBundle.write({
|
|
48
|
+
file: tempSSRFile,
|
|
49
|
+
format: 'es',
|
|
50
|
+
exports: 'default'
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// Import the SSR bundle
|
|
54
|
+
const { default: App } = await import(tempSSRFile);
|
|
55
|
+
const { html: initialHtml } = App.render();
|
|
56
|
+
|
|
57
|
+
// Clean up temp file
|
|
58
|
+
await fs.rm(tempDir, { recursive: true, force: true });
|
|
59
|
+
|
|
60
|
+
// Build client-side bundle
|
|
61
|
+
const clientBundle = await rollup({
|
|
62
|
+
input: svelteFilePath,
|
|
63
|
+
plugins: [
|
|
64
|
+
svelte({
|
|
65
|
+
compilerOptions: {
|
|
66
|
+
hydratable: true,
|
|
67
|
+
css: false
|
|
68
|
+
},
|
|
69
|
+
emitCss: true
|
|
70
|
+
}),
|
|
71
|
+
css({
|
|
72
|
+
output: function(styles) {
|
|
73
|
+
cssText = styles;
|
|
74
|
+
}
|
|
75
|
+
}),
|
|
76
|
+
resolve({
|
|
77
|
+
browser: true,
|
|
78
|
+
dedupe: ['svelte']
|
|
79
|
+
}),
|
|
80
|
+
commonjs(),
|
|
81
|
+
terser()
|
|
82
|
+
]
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
const { output: [{ code: clientCode }] } = await clientBundle.generate({
|
|
86
|
+
format: 'iife',
|
|
87
|
+
name: 'App',
|
|
88
|
+
globals: {
|
|
89
|
+
svelte: 'Svelte'
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
// Create the final HTML
|
|
94
|
+
const finalHtml = `<!DOCTYPE html>
|
|
95
|
+
<html lang="en">
|
|
96
|
+
<head>
|
|
97
|
+
<meta charset="UTF-8">
|
|
98
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
99
|
+
<title>Static Svelte App</title>
|
|
100
|
+
<style>
|
|
101
|
+
${cssText}
|
|
102
|
+
</style>
|
|
103
|
+
</head>
|
|
104
|
+
<body>
|
|
105
|
+
<div id="app">${initialHtml}</div>
|
|
106
|
+
<script>
|
|
107
|
+
${clientCode}
|
|
108
|
+
const app = new App({target: document.getElementById('app'), hydrate: true});
|
|
109
|
+
</script>
|
|
110
|
+
</body>
|
|
111
|
+
</html>`;
|
|
112
|
+
|
|
113
|
+
// Write the output file
|
|
114
|
+
const outputPath = path.join(outputDir, 'output.html');
|
|
115
|
+
await fs.writeFile(outputPath, finalHtml, 'utf-8');
|
|
116
|
+
} catch (error) {
|
|
117
|
+
throw error;
|
|
118
|
+
}
|
|
119
|
+
}
|
package/cli.js
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { program } from 'commander';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import fs from 'fs/promises';
|
|
6
|
+
import { createInterface } from 'readline';
|
|
7
|
+
import { buildStaticFile } from './bundle.js';
|
|
8
|
+
import { fileURLToPath } from 'url';
|
|
9
|
+
|
|
10
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
11
|
+
const __dirname = path.dirname(__filename);
|
|
12
|
+
|
|
13
|
+
// Create readline interface for prompts
|
|
14
|
+
const rl = createInterface({
|
|
15
|
+
input: process.stdin,
|
|
16
|
+
output: process.stdout
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
// Promisify readline question
|
|
20
|
+
const question = (query) => new Promise((resolve) => rl.question(query, resolve));
|
|
21
|
+
|
|
22
|
+
program
|
|
23
|
+
.name('svelte-bundle')
|
|
24
|
+
.description('Bundle a Svelte file into a standalone .html file')
|
|
25
|
+
.version('0.0.1')
|
|
26
|
+
.requiredOption('-i, --input <path>', 'Input Svelte file')
|
|
27
|
+
.option('-o, --output <path>', 'Output directory (defaults to current directory)')
|
|
28
|
+
.option('-f, --force', 'Force overwrite without asking');
|
|
29
|
+
|
|
30
|
+
program.parse();
|
|
31
|
+
|
|
32
|
+
const options = program.opts();
|
|
33
|
+
|
|
34
|
+
async function checkFileExists(filepath) {
|
|
35
|
+
try {
|
|
36
|
+
await fs.access(filepath);
|
|
37
|
+
return true;
|
|
38
|
+
} catch {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async function shouldOverwrite(filepath) {
|
|
44
|
+
const answer = await question(
|
|
45
|
+
chalk.yellow(`File ${filepath} already exists. Overwrite? (y/N): `)
|
|
46
|
+
);
|
|
47
|
+
return answer.toLowerCase() === 'y';
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async function validateAndProcess() {
|
|
51
|
+
try {
|
|
52
|
+
// Validate input file
|
|
53
|
+
const inputPath = path.resolve(options.input);
|
|
54
|
+
const inputExt = path.extname(inputPath);
|
|
55
|
+
|
|
56
|
+
if (inputExt !== '.svelte') {
|
|
57
|
+
console.error(chalk.red('Error: Input file must be a .svelte file'));
|
|
58
|
+
process.exit(1);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Check if input file exists
|
|
62
|
+
try {
|
|
63
|
+
await fs.access(inputPath);
|
|
64
|
+
} catch {
|
|
65
|
+
console.error(chalk.red(`Error: Input file ${inputPath} does not exist`));
|
|
66
|
+
process.exit(1);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Determine output directory and file path
|
|
70
|
+
let outputDir = process.cwd();
|
|
71
|
+
if (options.output) {
|
|
72
|
+
outputDir = path.resolve(options.output);
|
|
73
|
+
// Create output directory if it doesn't exist
|
|
74
|
+
await fs.mkdir(outputDir, { recursive: true });
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const outputPath = path.join(outputDir, 'output.html');
|
|
78
|
+
|
|
79
|
+
// Check if output file exists and handle overwriting
|
|
80
|
+
if (await checkFileExists(outputPath)) {
|
|
81
|
+
if (!options.force) {
|
|
82
|
+
const shouldProceed = await shouldOverwrite(outputPath);
|
|
83
|
+
if (!shouldProceed) {
|
|
84
|
+
console.log(chalk.yellow('Operation cancelled.'));
|
|
85
|
+
process.exit(0);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
console.log(chalk.blue('Starting build process...'));
|
|
91
|
+
console.log(chalk.gray(`Input: ${inputPath}`));
|
|
92
|
+
console.log(chalk.gray(`Output: ${outputPath}`));
|
|
93
|
+
|
|
94
|
+
// Process the file
|
|
95
|
+
await buildStaticFile(inputPath, outputDir);
|
|
96
|
+
|
|
97
|
+
console.log(chalk.green('\n✨ Build completed successfully!'));
|
|
98
|
+
console.log(chalk.gray(`Output file: ${outputPath}`));
|
|
99
|
+
} catch (error) {
|
|
100
|
+
console.error(chalk.red('\nBuild failed:'));
|
|
101
|
+
console.error(chalk.red(error.message));
|
|
102
|
+
process.exit(1);
|
|
103
|
+
} finally {
|
|
104
|
+
rl.close();
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
validateAndProcess();
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "svelte-bundle",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Bundle Svelte files into static HTML files",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"svelte-bundle": "./cli.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"cli.js",
|
|
11
|
+
"bundle.js"
|
|
12
|
+
],
|
|
13
|
+
"keywords": [
|
|
14
|
+
"svelte",
|
|
15
|
+
"bundle",
|
|
16
|
+
"static",
|
|
17
|
+
"html",
|
|
18
|
+
"ssr"
|
|
19
|
+
],
|
|
20
|
+
"publishConfig": {
|
|
21
|
+
"access": "public"
|
|
22
|
+
},
|
|
23
|
+
"scripts": {
|
|
24
|
+
"prepublishOnly": "chmod +x cli.js"
|
|
25
|
+
},
|
|
26
|
+
"author": "Teddy Hartling",
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"@rollup/plugin-commonjs": "^24.0.1",
|
|
30
|
+
"@rollup/plugin-node-resolve": "^15.0.2",
|
|
31
|
+
"@rollup/plugin-terser": "^0.4.0",
|
|
32
|
+
"chalk": "^5.3.0",
|
|
33
|
+
"commander": "^11.0.0",
|
|
34
|
+
"rollup": "^3.20.0",
|
|
35
|
+
"rollup-plugin-css-only": "^4.3.0",
|
|
36
|
+
"rollup-plugin-svelte": "^7.1.4",
|
|
37
|
+
"svelte": "^3.58.0"
|
|
38
|
+
}
|
|
39
|
+
}
|