├── .gitignore ├── LICENSE ├── README.md ├── cli.ts ├── imports ├── fmt.ts ├── fs.ts └── path.ts ├── src ├── cli │ ├── create.ts │ ├── io.ts │ ├── prompt.ts │ └── templates.ts └── shared │ ├── encoder.ts │ ├── log.ts │ ├── types.ts │ ├── utils.ts │ └── version.ts └── static ├── hotreloading.gif └── svelte-logo.svg /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/ 2 | compiler/example/App.js 3 | tests/ 4 | App/ 5 | Ssg/ 6 | test.ts -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Crew Dev 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

Snel 🦕

2 | 3 |

4 | 5 |

A Cybernetical tool for svelte applications on deno in deno (Snel = fast in Nederlands)

6 |

7 | 8 |

9 | 10 | GitHub issues 11 | 12 | 13 | GitHub forks 14 | 15 | 16 | GitHub stars 17 | 18 | 19 | GitHub license 20 | 21 | 22 | 23 | 24 |

25 | 26 | ## What is Snel? 27 | 28 | It is a `tool/framework` to compile .svelte component to javascript files to create web application using deno, vite and svelte 29 | 30 | ## Main features 31 | 32 | - simple setup 33 | - quick compilation 34 | - hot reloading 35 | - [import maps](https://github.com/WICG/import-maps) support (WIP) 36 | - support for scss and less out of the box (WIP) 37 | - support for typescript 38 | - [SSG](docs/ssg.md) (WIP) 39 | - SSR (WIP) 40 | 41 | ## What do I need to start using Snel? 42 | 43 | the only thing you need is to run the installation command. 44 | 45 | ```console 46 | deno install -A -n snel https://deno.land/x/snel/install.ts 47 | ``` 48 | 49 | ## how to create a project with Snel? 50 | 51 | after running the installation script you just have to run the create command: 52 | 53 | ```console 54 | snel create [project name] 55 | ``` 56 | 57 | then you just have to enter the created project and start the development server 58 | 59 | ```console 60 | cd ./[project name] 61 | 62 | deno task start 63 | ``` 64 | 65 | this starts your application on a development server in the port you entered in the configuration 66 | 67 | ## Using svelte core libraries 68 | 69 | to use svelte core libraries such as transition, store, motion etc. must be called using the following syntax: 70 | 71 | ```javascript 72 | import { cubicOut } from "svelte/easing"; 73 | import { tweened } from "svelte/motion"; 74 | import { onMount } from "svelte"; 75 | ``` 76 | 77 | `svelte` tells the compiler that svelte core resources are being accessed. 78 | 79 | ## Using import maps 80 | 81 | You can use import maps to reference the dependencies you use, to use import maps from bes have an `import_map.json` file with the following structure: 82 | 83 | ```json 84 | { 85 | "imports": { 86 | "[package name]": "[package url]" 87 | } 88 | } 89 | ``` 90 | 91 | In order for the compiler to know that you are using import maps, you need to import the dependencies as follows: 92 | 93 | ```javascript 94 | import moment from "moment"; 95 | import axios from "axios"; 96 | ``` 97 | 98 | > **note**: you can use import maps inside svelte components 99 | 100 | ## Snel config file 101 | 102 | snel uses a configuration file to define different things, like the development server port or add different plugins, this file can be named as `snel.config.js` or `snel.config.ts`. 103 | 104 | `example: snel.config.js` 105 | 106 | ```javascript 107 | export default { 108 | port: 3000, // development server port 109 | mode: "dom", // type project "dom" | "ssg" 110 | plugins: [], // plugins must be compatible with rollup deno 111 | extendsImportMap: [ 112 | // extends import map using externas import maps 113 | "https://denopkg.com/crewdevio/Snel-carbon@main/components.map.json", 114 | ], 115 | }; 116 | ``` 117 | 118 | config options: 119 | 120 | - port (number): port number for development server 121 | - mode (string): type project "dom" | "ssg" 122 | - plugins (Plugin[ ]): plugins must be compatible with [rollup deno](https://deno.land/x/drollup) 123 | - extendsImportMap (string[ ]): extends from externas import maps 124 | 125 | if you want to use `snel.config.ts` you can import `snelConfig` interface to provide type checking: 126 | 127 | `example: snel.config.ts` 128 | 129 | ```typescript 130 | import type { snelConfig } from "https://deno.land/x/snel/mod.ts"; 131 | 132 | export default { 133 | ... 134 | }; 135 | ``` 136 | 137 | ## Manage import maps dependencies using [trex](https://github.com/crewdevio/Trex) 138 | 139 | if you don't have an import map.json file you can create it using the `trex install` command, trex is mainly focused on handling dependencies for `deno` but this doesn't prevent you from being able to use it to handle your dependencies for `snel/svelte`. to install any dependency you just have to use the [custom command](https://github.com/crewdevio/Trex#adding-custom-packages) from trex: 140 | 141 | ```console 142 | trex --custom axios=https://cdn.skypack.dev/axios 143 | ``` 144 | 145 | this will install axios and it will make it accessible in the import map file: 146 | 147 | ```json 148 | { 149 | "imports": { 150 | "axios": "https://cdn.skypack.dev/axios" 151 | } 152 | } 153 | ``` 154 | 155 | > **note**: You should know that your dependencies must be compatible with [es modules](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import) and with the browser, if you refer to some import maps package and it is not found by the compiler, it will not be transformed, so an error will appear in your browser. 156 | 157 | we recommend these sites for you to install your dependencies 158 | 159 | - [skypack.dev](https://www.skypack.dev/) 160 | - [esm.sh](https://esm.sh/) 161 | - [jsdelivr.com](https://www.jsdelivr.com/) 162 | 163 | ## Typescript, Sass and Less support 164 | 165 | snel supports typescript out the box, so you can import typescript files into `.svelte` components without specifying the use of typescript within the component. 166 | 167 | `App.svelte` 168 | 169 | ```html 170 | 173 | 174 |

PI is = {PI}

175 | 176 | 181 | ``` 182 | 183 | `pi.ts` 184 | 185 | ```typescript 186 | export const PI: number = Math.PI; 187 | ``` 188 | 189 | Something important to know is that if you are going to import from typescript files without specifying the use of typescript within the component, you can only import non-types elements, example: 190 | 191 | - types 192 | - interfaces 193 | 194 | in case you want to use the typescript inside the components, you just have to specify it in the `lang` attribute: 195 | 196 | ```html 197 | 209 | ``` 210 | 211 | to import types and interfaces into components these must be specified using the following syntax: 212 | 213 | ```html 214 | 217 | ``` 218 | 219 | and you should only import types using this syntax and not combine imports with other elements. 220 | 221 | ```html 222 | 230 | ``` 231 | 232 | > **note**: typescript support within components is not stable and compilation errors in hot reloading may appear. 233 | 234 | in the same way you can use the syntax of sass and less inside the components to define the styles. 235 | 236 | ```html 237 | 240 | 241 | 242 | 243 | 246 | ``` 247 | 248 | > **note**: for now importing external styles is not available for css, sass and less. 249 | 250 | ## Import components and files 251 | 252 | when you create a project with snel you will notice that the components are imported as follows: 253 | 254 | `example` 255 | 256 | ```javascript 257 | // App.svelte 258 | import Home from "@/components/Home.svelte"; 259 | ``` 260 | 261 | `@/` It is equivalent to `./`, Similarly, if you need to access any component or file that is inside the src folder, you can use the following syntax: 262 | 263 | `example` 264 | 265 | ```javascript 266 | // src/Users.ts 267 | export default class Users { 268 | ... 269 | } 270 | ``` 271 | 272 | ```javascript 273 | // src/components/views/content/User.svelte 274 | import Users from "~/Users.ts"; 275 | ``` 276 | 277 | this would be equivalent to: 278 | 279 | ```javascript 280 | import Users from "../../../Users.ts"; 281 | ``` 282 | 283 | summary: 284 | 285 | - `@/` 286 | - equivalent to `./` 287 | - `~/` 288 | - equivalent to `[current work directory]/src/` 289 | - `$/` 290 | - equivalent to `[current work directory]/` 291 | 292 | this syntax can be used in javascript, typescript files and components. 293 | 294 | > **note**: you can change the behavior by rewriting the pattern inside the import_map.json file, although be careful when doing this. 295 | 296 | ## Deploy SPA app 297 | 298 | If you want to have a simple workflow to deploy your SPA application, you can use this. 299 | 300 | `deploy` 301 | 302 | ```yml 303 | name: Build and Deploy 304 | on: 305 | push: 306 | branches: 307 | - main 308 | jobs: 309 | build: 310 | runs-on: windows-latest 311 | steps: 312 | - name: Checkout 🛎️ 313 | uses: actions/checkout@v2.3.1 314 | 315 | - name: Install Deno 316 | uses: denolib/setup-deno@v2 317 | with: 318 | deno-version: v1.x 319 | 320 | - name: Build 321 | run: | 322 | deno run --allow-run --allow-read https://deno.land/x/snel/install.ts 323 | trex run build 324 | - name: Upload Artifacts 🔺 325 | uses: actions/upload-artifact@v1 326 | with: 327 | name: site 328 | path: dist 329 | 330 | deploy: 331 | needs: [build] 332 | runs-on: ubuntu-latest 333 | steps: 334 | - name: Checkout 🛎️ 335 | uses: actions/checkout@v2.3.1 336 | 337 | - name: Download Artifacts 🔻 338 | uses: actions/download-artifact@v1 339 | with: 340 | name: site 341 | 342 | - name: Deploy 🚀 343 | uses: JamesIves/github-pages-deploy-action@4.1.4 344 | with: 345 | ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }} 346 | branch: deploy 347 | folder: "site" 348 | clean-exclude: | 349 | vercel.json 350 | manifest.json 351 | ``` 352 | 353 | this compiles the application every time it is pushed to the `main` branch, and uploads the compiled files to the `deploy` branch, which could be deployed in hosts such as `vercel` or `netlify`. 354 | 355 | If you are using the snel client router and you are going to deploy vercel or netlify you must add the respective configuration files to avoid having 404 errors. 356 | 357 | `for vercel: vercel.json` 358 | 359 | ```json 360 | { 361 | "routes": [ 362 | { "handle": "filesystem" }, 363 | { "src": "/.*", "dest": "/index.html" } 364 | ] 365 | } 366 | ``` 367 | 368 | `for netlify: _redirects` 369 | 370 | ```console 371 | /* /index.html 200 372 | ``` 373 | 374 | to avoid this files was removed in each deploy, you can ignore specific files: 375 | 376 | ```yml 377 | .... 378 | - name: Deploy 🚀 379 | uses: JamesIves/github-pages-deploy-action@4.1.4 380 | with: 381 | ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }} 382 | branch: deploy 383 | folder: "site" 384 | clean-exclude: | 385 | vercel.json # <- ignore vercel.json and manifest.json file 386 | manifest.json 387 | ``` 388 | 389 | for github pages you can use this trick: 390 | 391 | create a `404.html` file: 392 | 393 | ```html 394 | 395 | 396 | 397 | 398 | .... 399 | 402 | 403 | 404 | 405 |               406 |               407 |               408 | 409 | 410 | ``` 411 | 412 | and in your `index.html` add this: 413 | 414 | ```html 415 | 416 | 417 | 418 | 419 | .... 420 | 425 | 426 | 427 | ..... 428 | 448 | 449 | 450 | ``` 451 | 452 | ## Hot Reloading 453 | 454 | Snel provides hot reload capability, it compiles and updates the application when changes are applied to it 455 | 456 | `example` 457 | 458 | ![img hot reload](https://raw.githubusercontent.com/crewdevio/Snel/main/static/hotreloading.gif) 459 | -------------------------------------------------------------------------------- /cli.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Crew Dev. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | * 7 | */ 8 | 9 | import { CommandNotFound, HelpCommand } from "./src/shared/log.ts"; 10 | import { VERSION as cliVersion } from "./src/shared/version.ts"; 11 | import { flags, keyWords } from "./src/shared/utils.ts"; 12 | import { CreateProject } from "./src/cli/create.ts"; 13 | import { PromptConfig } from "./src/cli/prompt.ts"; 14 | import { showHelp } from "./src/shared/utils.ts"; 15 | import { colors } from "./imports/fmt.ts"; 16 | 17 | const { args: Args } = Deno; 18 | type Command = "create"; 19 | const command = Args[0] as Command; 20 | 21 | const instructs = { 22 | // create a template project 23 | async create() { 24 | if (flags.help.includes(Args[1])) { 25 | return HelpCommand({ 26 | command: { 27 | alias: [keyWords.create], 28 | description: "create a template project", 29 | }, 30 | flags: [{ alias: flags.help, description: "show command help" }], 31 | }); 32 | } else await CreateProject({ ...PromptConfig(), projectName: Args[1] }); 33 | }, 34 | }; 35 | 36 | async function Main() { 37 | try { 38 | // execute instructions 39 | if (command === "create") { 40 | return await instructs.create(); 41 | } 42 | 43 | // show version 44 | else if (flags.version.includes(Args[0])) { 45 | console.log( 46 | colors.green( 47 | `snel: ${colors.yellow(cliVersion)}\ndeno: ${colors.yellow( 48 | Deno.version.deno 49 | )}` 50 | ) 51 | ); 52 | } 53 | // show help 54 | else if (flags.help.includes(Args[0]) || !Args[0]) { 55 | showHelp(); 56 | } else { 57 | CommandNotFound({ 58 | commands: [keyWords.create], 59 | flags: [...flags.help, ...flags.version], 60 | }); 61 | } 62 | } catch (error: any) { 63 | if (!(error instanceof Deno.errors.NotFound)) { 64 | console.log(error); 65 | } 66 | } 67 | } 68 | 69 | if (import.meta.main) { 70 | await Main(); 71 | } 72 | -------------------------------------------------------------------------------- /imports/fmt.ts: -------------------------------------------------------------------------------- 1 | export * as colors from "https://deno.land/std@0.213.0/fmt/colors.ts"; 2 | -------------------------------------------------------------------------------- /imports/fs.ts: -------------------------------------------------------------------------------- 1 | export * from "https://deno.land/std@0.213.0/fs/mod.ts"; 2 | -------------------------------------------------------------------------------- /imports/path.ts: -------------------------------------------------------------------------------- 1 | export * from "https://deno.land/std@0.213.0/path/mod.ts"; 2 | -------------------------------------------------------------------------------- /src/cli/create.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Crew Dev. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | * 7 | */ 8 | 9 | import { 10 | globalCss, 11 | indexHtml, 12 | rootSvelte, 13 | Home, 14 | mainjs, 15 | SVG, 16 | DTS, 17 | JSCONFIg, 18 | VITE, 19 | DENO, 20 | SVELTE_CONFIG, 21 | gitIgnore, 22 | } from "./templates.ts"; 23 | import type { CreateProjectOptions } from "../shared/types.ts"; 24 | import { createDir, createFile } from "./io.ts"; 25 | import { colors } from "../../imports/fmt.ts"; 26 | import { join } from "../../imports/path.ts"; 27 | 28 | export async function CreateProject(options: CreateProjectOptions) { 29 | const { projectName, port } = options; 30 | 31 | const startTime = Date.now(); 32 | const projectRoot = `${Deno.cwd()}/${projectName}`; 33 | 34 | const builder = { 35 | domFiles: [ 36 | { 37 | name: "index.html", 38 | path: projectRoot, 39 | source: indexHtml(), 40 | }, 41 | { 42 | name: "app.css", 43 | path: `${projectRoot}/src/`, 44 | source: globalCss, 45 | }, 46 | { 47 | name: "vite-env.d.ts", 48 | path: `${projectRoot}/src/`, 49 | source: DTS, 50 | }, 51 | { 52 | name: "jsconfig.json", 53 | path: projectRoot, 54 | source: JSCONFIg, 55 | }, 56 | { 57 | name: "App.svelte", 58 | path: `${projectRoot}/src/`, 59 | source: rootSvelte, 60 | }, 61 | { 62 | name: "Counter.svelte", 63 | path: `${projectRoot}/src/components`, 64 | source: Home, 65 | }, 66 | { 67 | name: "main.ts", 68 | path: `${projectRoot}/src/`, 69 | source: mainjs(), 70 | }, 71 | { 72 | name: "vite.config.mts", 73 | path: projectRoot, 74 | source: VITE(port), 75 | }, 76 | { 77 | name: "deno.json", 78 | path: projectRoot, 79 | source: DENO, 80 | }, 81 | { 82 | name: "svelte.config.js", 83 | path: projectRoot, 84 | source: SVELTE_CONFIG, 85 | }, 86 | { 87 | name: "svelte.svg", 88 | path: `${projectRoot}/src/assets/`, 89 | source: SVG, 90 | }, 91 | { 92 | name: ".gitignore", 93 | path: projectRoot, 94 | source: gitIgnore, 95 | }, 96 | ], 97 | domDir: [ 98 | { 99 | name: "public", 100 | path: `${Deno.cwd()}/${projectName}`, 101 | }, 102 | { 103 | name: projectName, 104 | path: Deno.cwd(), 105 | }, 106 | { 107 | name: "src", 108 | path: `${Deno.cwd()}/${projectName}`, 109 | }, 110 | { 111 | name: "components", 112 | path: `${Deno.cwd()}/${projectName}/src`, 113 | }, 114 | { 115 | name: "assets", 116 | path: `${Deno.cwd()}/${projectName}/src`, 117 | }, 118 | ], 119 | files() { 120 | return [...this.domFiles]; 121 | }, 122 | dirs() { 123 | return [...this.domDir]; 124 | }, 125 | }; 126 | 127 | for (const dir of builder.dirs()) { 128 | const { name, path } = dir; 129 | await createDir(name, path); 130 | } 131 | 132 | for (const file of builder.files()) { 133 | const { name, path, source } = file; 134 | await createFile(name, path, source as string); 135 | } 136 | 137 | const endTime = Date.now(); 138 | 139 | console.clear(); 140 | console.log(` 141 | Done in ${(endTime - startTime) / 1000}s. 142 | 143 | Success! Created ${projectName} at ${join(Deno.cwd(), projectName)} 144 | Inside that directory, you can run several commands: 145 | 146 | ${colors.blue("deno task dev")} (HMR) 147 | Starts the development server. 148 | 149 | ${colors.blue("deno task build")} 150 | Bundles the app into static files for production. 151 | 152 | ${colors.blue("deno task preview")} 153 | Compile the project in preview mode. 154 | 155 | We suggest that you begin by typing: 156 | 157 | ${colors.blue("cd")} ${projectName} 158 | ${colors.blue("deno task dev")} 159 | `); 160 | } 161 | -------------------------------------------------------------------------------- /src/cli/io.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Crew Dev. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | * 7 | */ 8 | 9 | import { join } from "../../imports/path.ts"; 10 | 11 | export async function createFile(name: string, path: string, source: string) { 12 | try { 13 | const file = await Deno.create(join(path, name)); 14 | const encoder = new TextEncoder(); 15 | 16 | await file.write(encoder.encode(source)); 17 | 18 | return true; 19 | } catch (error: unknown) { 20 | console.log(error); 21 | } 22 | } 23 | 24 | export async function createDir(name: string, path: string) { 25 | try { 26 | await Deno.mkdir(join(path, name)); 27 | 28 | return true; 29 | } catch (error: unknown) { 30 | console.log(error); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/cli/prompt.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Crew Dev. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | * 7 | */ 8 | 9 | import type { CreateProjectOptions } from "../shared/types.ts"; 10 | import { colors } from "../../imports/fmt.ts"; 11 | 12 | const isNumber = (n: string) => parseInt(n); 13 | 14 | function validate( 15 | fields: string[], 16 | field: string, 17 | question: string, 18 | type: "number" | "string" 19 | ): string | number { 20 | if (type === "number" && isNumber(field)) return parseInt(field); 21 | else if (fields.includes(field)) return field.trim().toLowerCase(); 22 | else { 23 | console.log( 24 | type === "string" 25 | ? colors.red( 26 | `\nthis field is no valid, these are valid "${fields.join(", ")}"\n` 27 | ) 28 | : colors.red("\nis not a valid number\n") 29 | ); 30 | 31 | return validate(fields, prompt(colors.green(question))!, question, type); 32 | } 33 | } 34 | 35 | export function PromptConfig( 36 | count = 0 37 | ): Omit { 38 | const answers: { [key: string]: string | number } = {}; 39 | 40 | const questions = [ 41 | { 42 | name: "port", 43 | message: "which port do you want the development server to run on?", 44 | _default: "3000", 45 | }, 46 | ]; 47 | 48 | for (const { message, name, _default } of questions) { 49 | switch (name) { 50 | case "port": 51 | answers[name] = validate( 52 | [], 53 | prompt(colors.green(message), _default)!, 54 | message, 55 | "number" 56 | ); 57 | break; 58 | } 59 | } 60 | 61 | for (const answer in answers) 62 | console.log(colors.green(`\n${answer}: ${answers[answer]}`)); 63 | 64 | const accept = confirm(colors.yellow("\nthis is the final configuration")); 65 | 66 | if (accept) return answers as Omit; 67 | 68 | return count <= 1 ? PromptConfig(count + 1) : Deno.exit(0); 69 | } 70 | 71 | export function notFoundConfig() { 72 | throw new Error( 73 | colors.red(`${colors.yellow("snel config")} file could not be found`) 74 | ).message; 75 | } 76 | 77 | export function serverLog({ dirName, port, localNet }: any) { 78 | console.clear(); 79 | console.log(` 80 | ${colors.green("Compiled successfully!")} 81 | 82 | You can now view ${colors.bold(colors.yellow(dirName))} in the browser. 83 | 84 | ${colors.bold("Local:")} http://localhost:${colors.bold( 85 | port.toString() 86 | )} 87 | ${localNet} 88 | 89 | Note that the development build is not optimized. 90 | To create a production build, use ${colors.bold( 91 | colors.blue("trex run build") 92 | )}. 93 | `); 94 | } 95 | -------------------------------------------------------------------------------- /src/cli/templates.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Crew Dev. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | * 7 | */ 8 | 9 | export const indexHtml = () => ` 10 | 11 | 12 | 13 | 14 | 15 | 16 | Vite + Deno + Svelte 17 | 18 | 19 |
20 | 21 | 22 | `; 23 | 24 | export const globalCss = `:root { 25 | font-family: Inter, Avenir, Helvetica, Arial, sans-serif; 26 | font-size: 16px; 27 | line-height: 24px; 28 | font-weight: 400; 29 | 30 | color-scheme: light dark; 31 | color: rgba(255, 255, 255, 0.87); 32 | background-color: #242424; 33 | 34 | font-synthesis: none; 35 | text-rendering: optimizeLegibility; 36 | -webkit-font-smoothing: antialiased; 37 | -moz-osx-font-smoothing: grayscale; 38 | -webkit-text-size-adjust: 100%; 39 | } 40 | 41 | a { 42 | font-weight: 500; 43 | color: #646cff; 44 | text-decoration: inherit; 45 | } 46 | a:hover { 47 | color: #535bf2; 48 | } 49 | 50 | body { 51 | margin: 0; 52 | display: flex; 53 | place-items: center; 54 | min-width: 320px; 55 | min-height: 100vh; 56 | } 57 | 58 | h1 { 59 | font-size: 3.2em; 60 | line-height: 1.1; 61 | } 62 | 63 | .card { 64 | padding: 2em; 65 | } 66 | 67 | #app { 68 | max-width: 1280px; 69 | margin: 0 auto; 70 | padding: 2rem; 71 | text-align: center; 72 | } 73 | 74 | button { 75 | border-radius: 8px; 76 | border: 1px solid transparent; 77 | padding: 0.6em 1.2em; 78 | font-size: 1em; 79 | font-weight: 500; 80 | font-family: inherit; 81 | background-color: #1a1a1a; 82 | cursor: pointer; 83 | transition: border-color 0.25s; 84 | } 85 | button:hover { 86 | border-color: #646cff; 87 | } 88 | button:focus, 89 | button:focus-visible { 90 | outline: 4px auto -webkit-focus-ring-color; 91 | } 92 | 93 | @media (prefers-color-scheme: light) { 94 | :root { 95 | color: #213547; 96 | background-color: #ffffff; 97 | } 98 | a:hover { 99 | color: #747bff; 100 | } 101 | button { 102 | background-color: #f9f9f9; 103 | } 104 | } 105 | `; 106 | 107 | export const rootSvelte = ` 111 | 112 |
113 |
114 | 115 | 116 | 117 |
118 |

Snel = Deno + Vite + Svelte

119 | 120 |
121 | 122 |
123 |
124 | 125 | 141 | `; 142 | 143 | export const Home = ` 149 | 150 | 153 | `; 154 | 155 | export const SVG = ``; 156 | 157 | export const DTS = `/// 158 | /// 159 | `; 160 | 161 | export const JSCONFIg = `{ 162 | "compilerOptions": { 163 | "moduleResolution": "Node", 164 | "target": "ESNext", 165 | "module": "ESNext", 166 | /** 167 | * svelte-preprocess cannot figure out whether you have 168 | * a value or a type, so tell TypeScript to enforce using 169 | */ 170 | "importsNotUsedAsValues": "error", 171 | "isolatedModules": true, 172 | "resolveJsonModule": true, 173 | /** 174 | * To have warnings / errors of the Svelte compiler at the 175 | * correct position, enable source maps by default. 176 | */ 177 | "sourceMap": true, 178 | "esModuleInterop": true, 179 | "skipLibCheck": true, 180 | "forceConsistentCasingInFileNames": true, 181 | "baseUrl": ".", 182 | /** 183 | * Disable this if you'd like to use dynamic types. 184 | */ 185 | "checkJs": true 186 | }, 187 | /** 188 | * Use global.d.ts instead of compilerOptions.types 189 | * to avoid limiting type declarations. 190 | */ 191 | "include": ["src/**/*.d.ts", "src/**/*.js", "src/**/*.svelte"] 192 | } 193 | `; 194 | 195 | export const VITE = (port?: number) => `import { defineConfig } from "npm:vite"; 196 | import { svelte } from "npm:@sveltejs/vite-plugin-svelte"; 197 | import "npm:svelte"; 198 | 199 | // https://vitejs.dev/config/ 200 | export default defineConfig({ 201 | server: { 202 | port: ${port ?? 3000}, 203 | }, 204 | plugins: [svelte()], 205 | }); 206 | `; 207 | 208 | export const DENO = `{ 209 | "tasks": { 210 | "dev": "deno run -A --node-modules-dir npm:vite", 211 | "build": "deno run -A --node-modules-dir npm:vite build", 212 | "preview": "deno run -A --node-modules-dir npm:vite preview", 213 | "serve": "deno run --allow-net --allow-read https://deno.land/std@0.161.0/http/file_server.ts dist/" 214 | } 215 | }`; 216 | 217 | export const SVELTE_CONFIG = `import { vitePreprocess } from "npm:@sveltejs/vite-plugin-svelte"; 218 | 219 | export default { 220 | // Consult https://svelte.dev/docs#compile-time-svelte-preprocess 221 | // for more information about preprocessors 222 | preprocess: vitePreprocess(), 223 | }; 224 | `; 225 | 226 | export const gitIgnore = (dir: string) => 227 | `/public/dist/ 228 | .DS_Store 229 | /${dir}`; 230 | 231 | export const mainjs = () => ` 232 | import "./app.css"; 233 | import App from "./App.svelte"; 234 | 235 | const app = new App({ 236 | target: document.getElementById("app"), 237 | }); 238 | 239 | export default app; 240 | `; 241 | -------------------------------------------------------------------------------- /src/shared/encoder.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Crew Dev. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | * 7 | */ 8 | 9 | export const decoder = new TextDecoder("utf-8"); 10 | 11 | export const encoder = new TextEncoder(); 12 | -------------------------------------------------------------------------------- /src/shared/log.ts: -------------------------------------------------------------------------------- 1 | import { colors } from "../../imports/fmt.ts"; 2 | 3 | const { yellow, red, green, blue } = colors; 4 | 5 | export interface HelpCommandParams { 6 | command: { 7 | alias: string[]; 8 | description: string; 9 | }; 10 | flags: Array<{ 11 | description: string; 12 | alias: string[]; 13 | }>; 14 | } 15 | 16 | export interface CommandNotFoundParams { 17 | commands: string[]; 18 | flags: string[]; 19 | } 20 | 21 | export function CommandNotFound({ commands, flags }: CommandNotFoundParams) { 22 | const { args } = Deno; 23 | 24 | const [command = "", flag = ""] = args; 25 | 26 | if (!commands.includes(command)) { 27 | console.log( 28 | red("Command not found:\n"), 29 | 30 | green( 31 | `\n${red("snel")} ${yellow( 32 | command ?? "empty command" 33 | )}: unknown command\n` 34 | ), 35 | 36 | green( 37 | `\nuse ${red("snel")} ${yellow("--help")} to see available commands\n` 38 | ) 39 | ); 40 | 41 | console.log(didYouMean(command, commands)); 42 | 43 | Deno.exit(0); 44 | } 45 | 46 | if (!flags.includes(flag)) { 47 | console.log( 48 | red("Command flag not found:\n"), 49 | 50 | green( 51 | `\n${red("snel")} ${yellow(command ?? "empty command")} ${yellow( 52 | flag 53 | )}: unknown command flag\n` 54 | ), 55 | 56 | green( 57 | `\nuse ${red("snel")} ${yellow(command ?? "empty command")} ${yellow( 58 | "--help" 59 | )} to see available command flags\n` 60 | ) 61 | ); 62 | 63 | console.log(didYouMean(flag, flags, command)); 64 | 65 | Deno.exit(0); 66 | } 67 | } 68 | 69 | export function HelpCommand({ command, flags }: HelpCommandParams) { 70 | console.log( 71 | green(`${red("action: ")} ${command.description}\n`), 72 | 73 | yellow("\nuse:\n"), 74 | 75 | green( 76 | `snel [${command.alias.map((cmd) => yellow(cmd))}] ${flags.map((flag) => 77 | `[${flag.alias.map((name) => yellow(name))}]`.replace(",", ", ") 78 | )}\n`.replace(",", " or ") 79 | ), 80 | 81 | yellow(flags.length ? "\nflags:\n" : ""), 82 | 83 | green( 84 | `${flags.map( 85 | (flag) => 86 | ` ${red("*")} [${flag.alias.map((sub) => yellow(sub))}] : ${ 87 | flag.description 88 | }\n` 89 | )}`.replaceAll(",", " ") 90 | ) 91 | ); 92 | } 93 | 94 | export const leven = (left: string, right: string) => { 95 | const array: any[] = []; 96 | const charCodeCache: any[] = []; 97 | 98 | if (left === right) { 99 | return 0; 100 | } 101 | 102 | const swap = left; 103 | 104 | if (left.length > right.length) { 105 | left = right; 106 | right = swap; 107 | } 108 | 109 | let leftLength = left.length; 110 | let rightLength = right.length; 111 | 112 | while ( 113 | leftLength > 0 && 114 | left.charCodeAt(~-leftLength) === right.charCodeAt(~-rightLength) 115 | ) { 116 | leftLength--; 117 | rightLength--; 118 | } 119 | 120 | let start = 0; 121 | 122 | while ( 123 | start < leftLength && 124 | left.charCodeAt(start) === right.charCodeAt(start) 125 | ) { 126 | start++; 127 | } 128 | 129 | leftLength -= start; 130 | rightLength -= start; 131 | 132 | if (leftLength === 0) { 133 | return rightLength; 134 | } 135 | 136 | let bCharCode; 137 | let result; 138 | let temp; 139 | let temp2; 140 | let i = 0; 141 | let j = 0; 142 | 143 | while (i < leftLength) { 144 | charCodeCache[i] = left.charCodeAt(start + i); 145 | array[i] = ++i; 146 | } 147 | 148 | while (j < rightLength) { 149 | bCharCode = right.charCodeAt(start + j); 150 | temp = j++; 151 | result = j; 152 | 153 | for (i = 0; i < leftLength; i++) { 154 | temp2 = bCharCode === charCodeCache[i] ? temp : temp + 1; 155 | temp = array[i]; 156 | 157 | result = array[i] = 158 | temp > result 159 | ? temp2 > result 160 | ? result + 1 161 | : temp2 162 | : temp2 > temp 163 | ? temp + 1 164 | : temp2; 165 | } 166 | } 167 | 168 | return result; 169 | }; 170 | 171 | /** 172 | * show spell suggestion 173 | * @param word 174 | * @param commands 175 | * @param command 176 | */ 177 | export const didYouMean = (word: string, commands: string[], command = "") => { 178 | const best = commands 179 | .filter((cmd) => leven(word, cmd) < word.length * 0.4) 180 | .map((str) => green(` snel ${yellow(command)} ${blue(str)}`)); 181 | return best.length === 0 182 | ? "" 183 | : best.length === 1 184 | ? `\n ${yellow("Did you mean this?")}\n\n${best[0]}` 185 | : `\nDid you mean one of these?\n${best.slice(0, 3).join("\n")}\n`; 186 | }; 187 | -------------------------------------------------------------------------------- /src/shared/types.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Crew Dev. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | * 7 | */ 8 | 9 | export interface CreateProjectOptions { 10 | port: number; 11 | projectName: string; 12 | } 13 | -------------------------------------------------------------------------------- /src/shared/utils.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Crew Dev. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | * 7 | */ 8 | 9 | import { colors } from "../../imports/fmt.ts"; 10 | 11 | export const flags = { 12 | help: ["--help", "-h"], 13 | version: ["-v", "--version"], 14 | }; 15 | 16 | export function showHelp() { 17 | console.log( 18 | colors.green( 19 | `A Cybernetical tool for svelte applications on deno 20 | 21 | USAGE: 22 | ${colors.white("snel")} ${colors.yellow("[SUBCOMMAND] [OPTIONS]")} 23 | 24 | OPTIONS: 25 | ${colors.yellow("-h, --help")} ${colors.white("print help info")} 26 | ${colors.yellow("-v, --version")} ${colors.white("print version")} 27 | 28 | SUBCOMMANDS: 29 | ${colors.yellow("create")} ${colors.white("create a template project")} 30 | ${colors.yellow("dev")} ${colors.white("build application in dev mode")} 31 | ${colors.yellow("serve")} ${colors.white( 32 | "build and server in a dev server" 33 | )} 34 | ${colors.yellow("build")} ${colors.white( 35 | "build application for production" 36 | )} 37 | 38 | you can see the different options available for each command using: 39 | snel ${colors.yellow("[command] --help or -h")} 40 | ` 41 | ) 42 | ); 43 | } 44 | 45 | export const keyWords = { 46 | create: "create", 47 | }; 48 | -------------------------------------------------------------------------------- /src/shared/version.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Crew Dev. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | * 7 | */ 8 | 9 | export const VERSION = "1.0.0"; 10 | -------------------------------------------------------------------------------- /static/hotreloading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crewdevio/Snel/45f7d13ee6c1828fdd40cba06e857cdb7a0c16d5/static/hotreloading.gif -------------------------------------------------------------------------------- /static/svelte-logo.svg: -------------------------------------------------------------------------------- 1 | --------------------------------------------------------------------------------