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.
Files changed (4) hide show
  1. package/ReadMe.md +38 -0
  2. package/bundle.js +119 -0
  3. package/cli.js +108 -0
  4. 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
+ }