├── .github
├── logo.svg
└── workflows
│ └── node.js.yml
├── .gitignore
├── CHANGELOG.md
├── README.md
├── fragments
├── auth
│ ├── blueprint.js
│ └── template
│ │ └── src
│ │ ├── components
│ │ └── Auth
│ │ │ ├── Link.svelte
│ │ │ ├── Login.svelte
│ │ │ └── store.js
│ │ └── pages
│ │ ├── _layout.svelte.fragment.js
│ │ ├── login.svelte
│ │ ├── protected
│ │ ├── _reset.svelte
│ │ └── index.svelte
│ │ └── user.svelte
├── autoPreprocess
│ ├── blueprint.js
│ └── package.json
├── base
│ ├── blueprint.js
│ ├── package.json
│ └── template
│ │ ├── .gitignore
│ │ ├── src
│ │ ├── App.svelte
│ │ ├── App.svelte.fragment.js
│ │ ├── main.js
│ │ └── pages
│ │ │ ├── _fallback.svelte
│ │ │ ├── _layout.svelte
│ │ │ ├── guide
│ │ │ ├── _layout.svelte
│ │ │ ├── index.svelte
│ │ │ └── routify.md
│ │ │ └── index.svelte
│ │ ├── static
│ │ ├── __app.html
│ │ └── favicon.ico
│ │ └── test
│ │ ├── build
│ │ └── README.md
│ │ ├── common
│ │ ├── README.md
│ │ └── base.test.js
│ │ ├── dev
│ │ └── README.md
│ │ └── lib
│ │ ├── index.js
│ │ └── wrapper.js
├── content
│ ├── blueprint.js
│ └── template
│ │ ├── src
│ │ └── pages
│ │ │ ├── introduction
│ │ │ ├── _layout.svelte
│ │ │ └── index.svelte
│ │ │ └── posts
│ │ │ ├── [slug].svelte
│ │ │ ├── _layout.svelte
│ │ │ ├── entries
│ │ │ ├── _layout.svelte
│ │ │ ├── html-ftw.svelte
│ │ │ ├── secretpost.svelte
│ │ │ └── something-about-html.svelte
│ │ │ ├── index.svelte
│ │ │ └── inlined.svelte
│ │ └── test
│ │ └── common
│ │ └── content.test.js
├── dev
│ └── blueprint.js
├── docker-nginx
│ ├── blueprint.js
│ └── template
│ │ └── Dockerfile
├── docker-ssr
│ ├── blueprint.js
│ └── template
│ │ ├── Dockerfile
│ │ └── test
│ │ └── build
│ │ └── docker-ssr.test.js
├── i18n
│ ├── blueprint.js
│ └── template
│ │ ├── src
│ │ ├── App.svelte.fragment.js
│ │ ├── components
│ │ │ └── Lang.svelte
│ │ └── pages
│ │ │ ├── _layout.svelte.fragment.js
│ │ │ └── guide
│ │ │ └── i18n.svelte
│ │ └── test
│ │ └── common
│ │ └── i18n.test.js
├── markdown
│ ├── blueprint.js
│ ├── package.json
│ └── template
│ │ ├── src
│ │ ├── components
│ │ │ └── Card.svelte
│ │ └── pages
│ │ │ └── guide
│ │ │ └── markdown.md
│ │ └── test
│ │ └── common
│ │ └── mdsvex.test.js
├── milligram
│ ├── blueprint.js
│ └── template
│ │ ├── src
│ │ └── pages
│ │ │ └── guide
│ │ │ └── milligram.svelte
│ │ └── static
│ │ └── __app.html.fragment.js
├── mobile
│ ├── blueprint.js
│ └── template
│ │ ├── src
│ │ ├── components
│ │ │ ├── Decorator.svelte
│ │ │ └── navigation
│ │ │ │ ├── Navbar.svelte
│ │ │ │ └── Overlay.svelte
│ │ └── pages
│ │ │ ├── _layout.svelte
│ │ │ ├── guide
│ │ │ └── mobile.svelte
│ │ │ ├── home
│ │ │ ├── _layout.svelte
│ │ │ └── index.svelte
│ │ │ └── index.svelte
│ │ └── static
│ │ ├── __app.html.fragment.js
│ │ └── mobile.css
├── navigation
│ ├── blueprint.js
│ └── template
│ │ ├── src
│ │ ├── components
│ │ │ └── Navbar
│ │ │ │ ├── Bar.svelte
│ │ │ │ └── Navigation.svelte
│ │ └── pages
│ │ │ └── _layout.svelte
│ │ └── test
│ │ └── common
│ │ └── navigation.test.js
├── nollup
│ ├── blueprint.js
│ └── template
│ │ ├── .nolluprc.js
│ │ ├── .nolluprc.js.fragment.js
│ │ ├── package.json
│ │ └── rollup.config.js.fragment.js
├── postcss
│ ├── blueprint.js
│ └── package.json
├── rollup
│ ├── blueprint.js
│ └── template
│ │ ├── package.json
│ │ ├── rollup.config.js
│ │ ├── rollup.config.js.fragment.js
│ │ ├── src
│ │ └── pages
│ │ │ └── guide
│ │ │ └── rollup.svelte
│ │ └── static
│ │ └── __app.html.fragment.js
├── scroll-navigation
│ ├── blueprint.js
│ └── template
│ │ └── src
│ │ └── pages
│ │ ├── _layout.svelte
│ │ ├── frontpage
│ │ ├── _layout.svelte
│ │ ├── contact.svelte
│ │ ├── features.svelte
│ │ ├── home.svelte
│ │ └── story.svelte
│ │ ├── guide
│ │ └── single-page.svelte
│ │ └── index.svelte
├── serviceworker
│ ├── blueprint.js
│ └── template
│ │ ├── src
│ │ ├── App.svelte.fragment.js
│ │ ├── components
│ │ │ └── Serviceworker.svelte
│ │ ├── pages
│ │ │ └── guide
│ │ │ │ └── serviceworker.md
│ │ └── sw.js
│ │ ├── static
│ │ ├── images
│ │ │ └── touch-icons
│ │ │ │ ├── logo-192.png
│ │ │ │ └── logo-800.png
│ │ └── manifest.json
│ │ └── workbox-config.js
├── snowpack
│ ├── blueprint.js
│ ├── package.json
│ └── template
│ │ ├── snowpack.config.js
│ │ ├── src
│ │ └── pages
│ │ │ └── guide
│ │ │ └── snowpack.md
│ │ └── static
│ │ └── __app.html.fragment.js
├── spank
│ ├── blueprint.js
│ ├── package.json
│ └── template
│ │ ├── src
│ │ └── pages
│ │ │ └── guide
│ │ │ └── spank.md
│ │ └── test
│ │ └── build
│ │ └── spank.test.js
├── tailwindcss
│ ├── blueprint.js
│ ├── package.json
│ └── template
│ │ ├── .browserslistrc
│ │ └── src
│ │ ├── App.svelte.fragment.js
│ │ └── pages
│ │ └── guide
│ │ └── tailwind.svelte
├── vercel
│ ├── blueprint.js
│ └── template
│ │ ├── api
│ │ └── vercel-ssr
│ │ │ ├── build.js
│ │ │ ├── index.js
│ │ │ └── package.json
│ │ └── vercel.json
├── vite
│ ├── blueprint.js
│ ├── package.json
│ └── template
│ │ ├── src
│ │ └── pages
│ │ │ └── guide
│ │ │ └── vite.md
│ │ └── static
│ │ └── __app.html.fragment.js
└── windicss
│ ├── blueprint.js
│ ├── package.json
│ └── template
│ └── src
│ └── pages
│ └── guide
│ └── windicss.svelte
├── lib
├── cli.js
├── generate-combos.js
└── prompts.js
├── package.json
└── test
└── index.js
/.github/logo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.github/workflows/node.js.yml:
--------------------------------------------------------------------------------
1 | # This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
3 |
4 | name: Node.js CI
5 |
6 | on:
7 | push:
8 | pull_request:
9 |
10 | jobs:
11 | build:
12 |
13 | runs-on: ubuntu-latest
14 |
15 | strategy:
16 | matrix:
17 | node-version: [15.x]
18 | # See supported Node.js release schedule at https://nodejs.org/en/about/releases/
19 |
20 | steps:
21 | - uses: actions/checkout@v2
22 | - name: Use Node.js ${{ matrix.node-version }}
23 | uses: actions/setup-node@v2
24 | with:
25 | node-version: ${{ matrix.node-version }}
26 | - run: npm i
27 | - run: npm test
28 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | **/node_modules
2 | **/output
3 | temp
4 | test/workspace
5 | .canvasit-temp
6 | templates
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4 |
5 | ### [0.1.8](https://github.com/roxiness/stackmix/compare/v0.1.7...v0.1.8) (2021-12-26)
6 |
7 |
8 | ### Bug Fixes
9 |
10 | * spank didn't work with vite ([3848b77](https://github.com/roxiness/stackmix/commit/3848b775cef3df1ebaf77eeb3c2c8645a3750413))
11 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | # Stackmix [preview]
6 | ### Template generator based on Svelte and Routify.
7 |
8 | ---
9 |
10 | Don't be fooled by the "prerelease". You should totally take it for a ride. 😎
11 |
12 | ## Getting started
13 | ```
14 | npx stackmix [app-name]
15 | cd [app-name]
16 | npm install
17 | npm run dev
18 | ```
19 |
20 | Remember to report any bugs back to us. 😉
21 |
22 | ---
23 |
24 | ## Getting started [for contributors and testers]
25 | ```bash
26 | $ git clone https://github.com/roxiness/stackmix.git
27 | $ cd stackmix
28 | $ npm install
29 | ```
30 |
31 | #### To build a template from fragments
32 | `node lib/cli`
33 |
34 | __The wizard can be skipped by providing fragments as arguments__
35 | `npm run generate -- -fragments rollup,i18n,navigation,content,miligram,mdsvex`
36 |
37 | __use `--watch` to update output on fragment change__
38 |
39 | ---
40 |
41 | **Given the variety of fragments, we would greatly appreciate contributors for this projects** 🙏
42 |
--------------------------------------------------------------------------------
/fragments/auth/blueprint.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | type: 'feature',
3 | }
4 |
--------------------------------------------------------------------------------
/fragments/auth/template/src/components/Auth/Link.svelte:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
8 | {#if $authenticating}
9 | authenticating...
10 | {:else if $user}
11 | {$user.username}
12 | {:else}
13 | Login
14 | {/if}
15 |
16 |
17 |
18 |
31 |
--------------------------------------------------------------------------------
/fragments/auth/template/src/components/Auth/Login.svelte:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
Login
11 |
20 |
21 |
--------------------------------------------------------------------------------
/fragments/auth/template/src/components/Auth/store.js:
--------------------------------------------------------------------------------
1 | import { writable } from 'svelte/store'
2 | export const user = writable(null)
3 | export const authenticating = writable(true)
4 | export const logout = () => {
5 | localStorage.removeItem('user')
6 | authenticate()
7 | }
8 |
9 | export const login = (username, password) => {
10 | authenticating.set(true)
11 | if (username === 'user@example.com' && password === 'pass') {
12 | const user = { username, token: 'abcdefg' }
13 | storeCredentials(user)
14 | // we're delaying authentication to simulate realworld timings
15 | setTimeout(authenticate, 500)
16 | } else {
17 | authenticating.set(false)
18 | console.error('wrong username or password')
19 | }
20 | }
21 |
22 | // we're delaying authentication to simulate realworld timings
23 | setTimeout(authenticate, 500)
24 |
25 | function authenticate() {
26 | /**
27 | * Insecure example!
28 | * In production, a token should be stored in localStorage/cookie
29 | * and sent to an auth server for verification.
30 | * */
31 | user.set(getCredentials())
32 |
33 | // we need to inform other components that we're no longer authenticating
34 | authenticating.set(false)
35 | }
36 |
37 | function getCredentials() {
38 | const cred = localStorage.getItem('user')
39 | return cred && JSON.parse(cred)
40 | }
41 |
42 | function storeCredentials(user) {
43 | localStorage.setItem('user', JSON.stringify(user))
44 | }
45 |
--------------------------------------------------------------------------------
/fragments/auth/template/src/pages/_layout.svelte.fragment.js:
--------------------------------------------------------------------------------
1 | module.exports.patch = ({ placeholders, configs }) => {
2 | placeholders.script.push(
3 | `import Link from '../components/Auth/Link.svelte'`,
4 | )
5 | placeholders.navigation.push(`
6 |
7 |
`)
8 | }
9 |
--------------------------------------------------------------------------------
/fragments/auth/template/src/pages/login.svelte:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/fragments/auth/template/src/pages/protected/_reset.svelte:
--------------------------------------------------------------------------------
1 |
17 |
18 |
19 |
Go back
20 |
21 | Protected module {#if $user}logout {/if}
23 |
24 |
25 | {#if $user}
26 |
27 | {:else if $authenticating}
28 | authenticating...
29 | {:else}
30 |
31 | {/if}
32 |
33 |
34 |
35 |
54 |
--------------------------------------------------------------------------------
/fragments/auth/template/src/pages/protected/index.svelte:
--------------------------------------------------------------------------------
1 | protected/index.svelte
2 |
3 | By checking for a user in
4 |
5 | _layout.svelte
6 | or
7 | _reset.svelte
we can render a conditional login component instead
8 | of the actual page.
9 |
10 | This ensures that we're not bouncing the user from URL to URL.
11 |
12 | If we need a dedicated login page, we can create a pages/login.svelte
14 | and use it to import our Login
component
15 |
16 |
--------------------------------------------------------------------------------
/fragments/auth/template/src/pages/user.svelte:
--------------------------------------------------------------------------------
1 |
11 |
12 | {#if $authenticating}
13 | authenticating...
14 | {:else if $user}
15 |
16 | Hello {$user.username}
17 |
18 | Logout
19 | {/if}
20 |
21 |
22 |
--------------------------------------------------------------------------------
/fragments/autoPreprocess/blueprint.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | type: 'feature',
3 | imports: {
4 | autoPreprocess: ['svelte-preprocess'],
5 | },
6 | configs: ({ getConfigString, $require }) => ({
7 | packagejson: require('./package.json'),
8 | svelte: {
9 | preprocess: [
10 | $require('autoPreprocess')(getConfigString('autoPreprocess')),
11 | ],
12 | },
13 | autoPreprocess: {
14 | /** config */
15 | },
16 | }),
17 | }
18 |
--------------------------------------------------------------------------------
/fragments/autoPreprocess/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "devDependencies": {
3 | "svelte-preprocess": "^4.6.3"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/fragments/base/blueprint.js:
--------------------------------------------------------------------------------
1 | // @ts-check
2 |
3 | /** @type {import('canvasit')['Blueprint']} */
4 | module.exports = {
5 | type: 'base',
6 | configs: () => ({
7 | options: { base: { useMarkdown: false } },
8 | test: {},
9 | svelte: {
10 | // Extract component CSS — better performance
11 | // css: "css => css.write(`bundle.css`)",
12 | preprocess: [],
13 | },
14 | packagejson: require('./package.json'),
15 | }),
16 | hooks: {
17 | afterPatch: async ctx => {
18 | ctx.writeTo(
19 | 'package.json',
20 | JSON.stringify(ctx.configs.packagejson, null, 2),
21 | )
22 | if (!ctx.configs.options.base.useMarkdown)
23 | await ctx.fileWalker(convertMarkdownToSvelte)
24 | },
25 | },
26 | }
27 |
28 | function convertMarkdownToSvelte(file) {
29 | if (file.ext === 'md') {
30 | const md = require('markdown-it')({ html: true })
31 | file.content = md.render(file.content)
32 | file.remove()
33 | file.filepath = file.filepath.replace(/\.[^.]+?$/, '.svelte')
34 | file.save()
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/fragments/base/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "routify-app",
3 | "version": "1.0.0",
4 | "@comments scripts": {
5 | "dev": "run all dev:* scripts",
6 | "dev:routify": "generate Routify's routes.js on filechange",
7 | "build": "run all build:* scripts",
8 | "build:routify": "build routify",
9 | "serve": "serve content in 'dist' folder"
10 | },
11 | "scripts": {
12 | "dev": "run-p dev:* serve:*",
13 | "dev:routify": "routify",
14 | "serve": "run-p serve:*",
15 | "serve:spa": "spassr",
16 | "serve:ssr": "spassr --ssr --port 5005",
17 | "build": "cross-env NODE_ENV=production run-s build:*",
18 | "build:routify": "routify -b",
19 | "test": "npm run test:dev && npm run test:build",
20 | "test:dev": "node test/lib/wrapper dev ava test/{common,dev}/**/*.test.js",
21 | "test:build": "node test/lib/wrapper build ava test/{common,build}/**/*.test.js"
22 | },
23 | "devDependencies": {
24 | "@roxi/routify": "^2.14.0",
25 | "fkill": "^7.1.0",
26 | "npm-run-all": "^4.1.5",
27 | "svelte": "^3.35.0",
28 | "cross-env": "^7.0.3",
29 | "spassr": "^2.4.1-0"
30 | },
31 | "routify": {
32 | "extensions": "svelte,html,svx,md"
33 | },
34 | "@comment options": [
35 | "config provides defaults for spassr, spank, tossr and poindexter.",
36 | "You can delete the field and this comment, if you use neither."
37 | ],
38 | "appConfig": {
39 | "port": 5000,
40 | "assetsDir": "static",
41 | "template": "static/__app.html",
42 | "distDir": "dist",
43 | "buildDir": "dist/build",
44 | "script": "dist/build/main.js"
45 | },
46 | "spassr": {
47 | "ssrOptions": {
48 | "inlineDynamicImports": true
49 | }
50 | },
51 | "ava": {
52 | "verbose": true
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/fragments/base/template/.gitignore:
--------------------------------------------------------------------------------
1 | **/node_modules/
2 | .DS_Store
3 | **/.history
4 | src/tmp/
5 | .routify
6 | .netlify
7 | assets/build
8 | .vercel
--------------------------------------------------------------------------------
/fragments/base/template/src/App.svelte:
--------------------------------------------------------------------------------
1 |
9 |
10 | __HTML__
11 |
12 |
15 |
--------------------------------------------------------------------------------
/fragments/base/template/src/App.svelte.fragment.js:
--------------------------------------------------------------------------------
1 | exports.patch = ({ placeholders, configs, stringify }) => {
2 | const config = configs.routifyRuntime
3 | if (config) {
4 | placeholders.script.push(`const config = ${stringify(config)}`)
5 | placeholders.html.push(` `)
6 | } else placeholders.html.push(` `)
7 | }
8 |
--------------------------------------------------------------------------------
/fragments/base/template/src/main.js:
--------------------------------------------------------------------------------
1 | import HMR from '@roxi/routify/hmr'
2 | import App from './App.svelte'
3 |
4 | const app = HMR(App, { target: document.body }, 'routify-app')
5 |
6 | export default app
7 |
--------------------------------------------------------------------------------
/fragments/base/template/src/pages/_fallback.svelte:
--------------------------------------------------------------------------------
1 | 404
2 |
--------------------------------------------------------------------------------
/fragments/base/template/src/pages/_layout.svelte:
--------------------------------------------------------------------------------
1 |
4 |
5 | __NAVIGATION__ __HEADER__
6 |
7 |
8 | __HTML__
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/fragments/base/template/src/pages/guide/_layout.svelte:
--------------------------------------------------------------------------------
1 |
11 |
12 |
13 |
Guide
14 |
15 |
16 | Fragments
17 | {#each fragments as { path, title }}
18 | {title}
19 | {/each}
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
56 |
--------------------------------------------------------------------------------
/fragments/base/template/src/pages/guide/index.svelte:
--------------------------------------------------------------------------------
1 | What's a fragment?
2 |
3 |
4 | A fragment is a template-component. By combining fragments, you can compose
5 | your own starter template.
6 |
7 |
8 |
9 | To see what's included in this template, have a look at the fragments to the
10 | left.
11 |
12 |
13 | You can remove this guide by deleting the src/pages/guide
folder.
15 |
--------------------------------------------------------------------------------
/fragments/base/template/src/pages/guide/routify.md:
--------------------------------------------------------------------------------
1 | ### Routify
2 |
3 | Routify is the router and along with Svelte it powers these templates.
4 |
5 | Routify outputs to `routify` by default.
6 |
7 | Configuration can be done in `package.json` and other places. For more info on configuration, see
8 | Routify - configuration
9 |
10 | For general usage, see Getting started .
11 |
--------------------------------------------------------------------------------
/fragments/base/template/src/pages/index.svelte:
--------------------------------------------------------------------------------
1 | App?!
2 | A short introduction would have been nice here...
3 |
4 | guide
5 |
--------------------------------------------------------------------------------
/fragments/base/template/static/__app.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Svelte app
8 |
9 |
10 |
11 |
12 |
13 |
14 | __HEAD__
15 |
16 |
17 |
18 | Please enable Javascript for best experience.
19 | __BODY__
20 |
21 |
22 |
--------------------------------------------------------------------------------
/fragments/base/template/static/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/roxiness/stackmix/7f0f0f2c4d2b57fa1ee37536c0f07f4e2b9d3beb/fragments/base/template/static/favicon.ico
--------------------------------------------------------------------------------
/fragments/base/template/test/build/README.md:
--------------------------------------------------------------------------------
1 | # dir for build tests
2 |
--------------------------------------------------------------------------------
/fragments/base/template/test/common/README.md:
--------------------------------------------------------------------------------
1 | # dir for build and dev tests
2 |
--------------------------------------------------------------------------------
/fragments/base/template/test/common/base.test.js:
--------------------------------------------------------------------------------
1 | /**
2 | * These tests require ava and playwright
3 | * To install:
4 | * npm install --save-dev ava playwright
5 | */
6 |
7 | const test = require('ava')
8 | const { pageMacro, baseUrl } = require('../lib')
9 |
10 | test('can see frontpage', pageMacro, async (t, page) => {
11 | await page.goto(baseUrl)
12 | t.assert(await page.waitForSelector('main'))
13 | })
14 |
--------------------------------------------------------------------------------
/fragments/base/template/test/dev/README.md:
--------------------------------------------------------------------------------
1 | # dir for dev tests
2 |
--------------------------------------------------------------------------------
/fragments/base/template/test/lib/index.js:
--------------------------------------------------------------------------------
1 | // create Page macro
2 | const pageMacro = (() => {
3 | let browserPromise
4 |
5 | const pageMacro = async (t, callback) => {
6 | const { chromium } = require('playwright')
7 |
8 | if (!browserPromise) browserPromise = chromium.launch()
9 |
10 | const browser = await browserPromise
11 | const page = await browser.newPage()
12 | page.setDefaultTimeout(1000)
13 | try {
14 | await callback(t, page)
15 | } finally {
16 | await page.close()
17 | }
18 | }
19 |
20 | return pageMacro
21 | })()
22 |
23 | /**
24 | * wait
25 | * @param {string|number} time
26 | * @returns {Promise}
27 | */
28 | const wait = time => new Promise(resolve => setTimeout(resolve, time))
29 |
30 | /**
31 | *
32 | * @param {string|number} port
33 | * @param {string|number} timeout
34 | * @returns {boolean}
35 | */
36 | async function checkPort(port, timeout) {
37 | const http = require('http')
38 | const timestamp = Date.now()
39 | let portFound = false
40 | while (!portFound && timestamp + timeout > Date.now()) {
41 | const req = http.request({ port })
42 | req.on('response', () => (portFound = true))
43 | req.on('error', err => {})
44 | req.end()
45 | await wait(500)
46 | }
47 | return portFound
48 | }
49 |
50 | /**
51 | * creates baseUrl
52 | * @returns {string}
53 | */
54 | function getBaseUrl() {
55 | const { port } = require('../../package.json').appConfig
56 | return `http://127.0.0.1:${port}/`
57 | }
58 |
59 | /**
60 | * returns absolute path from project-relative path
61 | * @param {...string} path
62 | * @returns {string}
63 | */
64 | function resolve(...path) {
65 | return require('path').resolve(__dirname, '..', '..', ...path)
66 | }
67 |
68 | function getPkgJson() {
69 | return require(resolve('package.json'))
70 | }
71 |
72 | module.exports = {
73 | baseUrl: getBaseUrl(),
74 | checkPort,
75 | wait,
76 | pageMacro,
77 | resolve,
78 | get pkgJson() {
79 | return getPkgJson()
80 | },
81 | get appConfig() {
82 | return getPkgJson().appConfig
83 | },
84 | }
85 |
--------------------------------------------------------------------------------
/fragments/base/template/test/lib/wrapper.js:
--------------------------------------------------------------------------------
1 | const { spawnSync, spawn } = require('child_process')
2 | const { checkPort, wait } = require('./')
3 | const fkill = require('fkill')
4 | const { port } = require('../../package.json').appConfig
5 | const timeout = 15000 // 15s
6 |
7 | const npm = /^win/.test(process.platform) ? 'npm.cmd' : 'npm'
8 | const npx = /^win/.test(process.platform) ? 'npx.cmd' : 'npx'
9 |
10 | // creates function with teardown script
11 | const createTeardown = pid => () => fkill(pid, { tree: true, force: true })
12 |
13 | const setups = {
14 | async dev() {
15 | // run dev server
16 | const child = await spawn(npm, ['run', 'dev'])
17 |
18 | if (await checkPort(port, timeout)) {
19 | await wait(500)
20 | return createTeardown(child.pid)
21 | } else {
22 | createTeardown(child.pid)()
23 | throw Error(`dev on port ${port} timed out after ${timeout} ms`)
24 | }
25 | },
26 | async build() {
27 | // build app
28 | spawnSync(npm, ['run', 'build'])
29 |
30 | // serve app
31 | const child = await spawn(npm, ['run', 'serve'])
32 |
33 | if (await checkPort(port, timeout)) {
34 | return createTeardown(child.pid)
35 | } else {
36 | createTeardown(child.pid)()
37 | throw Error(`build on port ${port} timed out after ${timeout} ms`)
38 | }
39 | },
40 | }
41 |
42 | // wrap the CLI command
43 | runCliCommand()
44 |
45 | async function runCliCommand() {
46 | // assign `dev` or `build` to mode and `ava` to cmd
47 | const [mode, cmd, ...args] = process.argv.slice(2)
48 |
49 | console.log(`[app] setup ${mode} test...`)
50 | const teardown = await setups[mode]()
51 | console.log(`[app] setup ${mode} test... Done.`)
52 | console.log(`[app] run: ${cmd} ${args.join(' ')}`)
53 | const { status } = spawnSync(npx, [cmd, ...args], { stdio: 'inherit' })
54 | console.log(`[app] teardown ${mode} test...`)
55 | teardown()
56 | console.log(`[app] teardown ${mode} test... Done.`)
57 | process.exit(status)
58 | }
59 |
--------------------------------------------------------------------------------
/fragments/content/blueprint.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | type: 'content',
3 | }
4 |
--------------------------------------------------------------------------------
/fragments/content/template/src/pages/introduction/_layout.svelte:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/fragments/content/template/src/pages/introduction/index.svelte:
--------------------------------------------------------------------------------
1 |
2 |
Introduction
3 |
4 |
Why hello
5 |
6 |
--------------------------------------------------------------------------------
/fragments/content/template/src/pages/posts/[slug].svelte:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 | {#if Post}
13 | {#await Post.component then page}
14 |
15 | {/await}
16 | {/if}
17 |
18 |
--------------------------------------------------------------------------------
/fragments/content/template/src/pages/posts/_layout.svelte:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/fragments/content/template/src/pages/posts/entries/_layout.svelte:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/fragments/content/template/src/pages/posts/entries/html-ftw.svelte:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | HTML FTW
6 |
7 | There are two axes differentiating various variations of HTML as currently
8 | specified: SGML-based HTML versus XML-based HTML (referred to as XHTML) on
9 | one axis, and strict versus transitional (loose) versus frameset on the
10 | other axis.
11 |
12 |
13 |
14 | To understand the subtle differences between HTML and XHTML, consider the
15 | transformation of a valid and well-formed XHTML 1.0 document that adheres to
16 | Appendix C (see below) into a valid HTML 4.01 document. To make this
17 | translation requires the following steps:
18 |
19 |
20 |
21 | Those are the main changes necessary to translate a document from XHTML 1.0
22 | to HTML 4.01. To translate from HTML to XHTML would also require the
23 | addition of any omitted opening or closing tags. Whether coding in HTML or
24 | XHTML it may just be best to always include the optional tags within an HTML
25 | document rather than remembering which tags can be omitted.
26 |
27 |
--------------------------------------------------------------------------------
/fragments/content/template/src/pages/posts/entries/secretpost.svelte:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/fragments/content/template/src/pages/posts/entries/something-about-html.svelte:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Something about HTML
6 |
7 | Most elements take the language-related attribute dir to specify text
8 | direction, such as with "rtl" for right-to-left text in, for example,
9 | Arabic, Persian or Hebrew.
10 |
11 |
12 |
13 | As of version 4.0, HTML defines a set of 252 character entity references and
14 | a set of 1,114,050 numeric character references, both of which allow
15 | individual characters to be written via simple markup, rather than
16 | literally. A literal character and its markup counterpart are considered
17 | equivalent and are rendered identically.
18 |
19 |
20 |
21 | HTML defines several data types for element content, such as script data and
22 | stylesheet data, and a plethora of types for attribute values, including
23 | IDs, names, URIs, numbers, units of length, languages, media descriptors,
24 | colors, character encodings, dates and times, and so on. All of these data
25 | types are specializations of character data.
26 |
27 |
--------------------------------------------------------------------------------
/fragments/content/template/src/pages/posts/index.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
10 |
Posts
11 |
12 | {#each posts as { meta, title, path }}
13 |
14 |
18 |
19 | {meta.summary || meta.frontmatter.summary}
20 | Read more.
22 |
23 | {meta.author || meta.frontmatter.author}
24 |
25 | {/each}
26 |
27 |
28 |
29 |
48 |
--------------------------------------------------------------------------------
/fragments/content/template/src/pages/posts/inlined.svelte:
--------------------------------------------------------------------------------
1 |
10 |
11 | {#each entries as post}
12 |
13 | {#await post.component then Post}
14 |
15 | {/await}
16 |
17 | {/each}
18 |
19 |
28 |
--------------------------------------------------------------------------------
/fragments/content/template/test/common/content.test.js:
--------------------------------------------------------------------------------
1 | /**
2 | * These tests require ava and playwright
3 | * To install:
4 | * npm install --save-dev ava playwright
5 | */
6 |
7 | const test = require('ava')
8 | const { pageMacro } = require('../lib')
9 |
10 | test('can see frontpage', pageMacro, async (t, page) => {
11 | await page.goto('http://localhost:5000/posts')
12 | t.assert(await page.waitForSelector('"something about html"'))
13 | })
14 |
--------------------------------------------------------------------------------
/fragments/dev/blueprint.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | configs: () => ({
3 | packagejson: { workspaces: ['../../*'] },
4 | }),
5 | }
6 |
--------------------------------------------------------------------------------
/fragments/docker-nginx/blueprint.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | type: 'feature',
3 | configs: () => ({
4 | packagejson: {
5 | scripts: {
6 | 'buildimage:docker':
7 | 'run-s build:* && docker build -t routify-app:latest .',
8 | 'run:docker':
9 | 'docker run --name routify-frontend -d -p 8080:80 routify-app:latest',
10 | },
11 | },
12 | }),
13 | }
14 |
--------------------------------------------------------------------------------
/fragments/docker-nginx/template/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM nginx
2 | COPY dist /usr/share/nginx/html
--------------------------------------------------------------------------------
/fragments/docker-ssr/blueprint.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | type: 'feature',
3 | configs: () => ({
4 | packagejson: {
5 | '@comments scripts': {
6 | 'build:docker': [
7 | 'Builds a docker image. To disable this from the',
8 | "build step, you can rename it to 'build-docker'",
9 | ],
10 | },
11 | scripts: {
12 | 'build:docker': 'docker build -t routify-app:latest .',
13 | 'run:docker':
14 | 'docker run --name routify -d -p 80:5005 routify-app:latest',
15 | },
16 | },
17 | }),
18 | }
19 |
--------------------------------------------------------------------------------
/fragments/docker-ssr/template/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:current-alpine
2 | RUN apk add --no-cache git
3 |
4 | RUN mkdir /code
5 | WORKDIR /code
6 |
7 | COPY . /code/
8 | RUN npm install
9 |
10 | CMD ["npm", "run", "serve:ssr"]
11 |
12 |
13 |
--------------------------------------------------------------------------------
/fragments/docker-ssr/template/test/build/docker-ssr.test.js:
--------------------------------------------------------------------------------
1 | /**
2 | * These tests require ava and playwright
3 | * To install:
4 | * npm install --save-dev ava playwright
5 | */
6 |
7 | const test = require('ava')
8 | const { pageMacro } = require('../pageMacro')
9 | const { checkPort } = require('../utils')
10 | const { spawnSync } = require('child_process')
11 |
12 | test.before(async t => {
13 | const npm = /^win/.test(process.platform) ? 'npm.cmd' : 'npm'
14 | spawnSync(npm, ['run', 'run:docker'], { stdio: 'inherit' })
15 | await checkPort(80, 15000)
16 | })
17 |
18 | test('can create an image', pageMacro, async (t, page) => {
19 | await page.goto('http://localhost:80')
20 | t.assert(await page.waitForSelector('main'))
21 | })
22 |
23 | test.after(async t => {
24 | spawnSync('docker', ['rm', '--force', 'routify'])
25 | })
26 |
--------------------------------------------------------------------------------
/fragments/i18n/blueprint.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | type: 'feature',
3 | configs: ({ getConfigString, stringify }) => ({
4 | routifyRuntime: {
5 | urlTransform: 'urlTransform',
6 | },
7 | }),
8 | }
9 |
--------------------------------------------------------------------------------
/fragments/i18n/template/src/App.svelte.fragment.js:
--------------------------------------------------------------------------------
1 | exports.patch = ({ placeholders, configs, stringify }) => {
2 | placeholders.script.push(
3 | `import { urlTransform } from './components/Lang.svelte'`,
4 | )
5 | }
6 |
--------------------------------------------------------------------------------
/fragments/i18n/template/src/components/Lang.svelte:
--------------------------------------------------------------------------------
1 |
41 |
42 |
48 |
49 |
50 | {#each Object.values(languages) as lang}
51 | {lang.long}
52 | {/each}
53 |
54 |
--------------------------------------------------------------------------------
/fragments/i18n/template/src/pages/_layout.svelte.fragment.js:
--------------------------------------------------------------------------------
1 | module.exports.patch = ({ placeholders, configs }) => {
2 | placeholders.script.push(`import Lang from '../components/Lang.svelte'`)
3 | placeholders.navigation.push(`
`)
4 | }
5 |
--------------------------------------------------------------------------------
/fragments/i18n/template/src/pages/guide/i18n.svelte:
--------------------------------------------------------------------------------
1 |
5 |
6 | {#if lang === 'en'}
7 | Internationalization
8 |
9 | In this example we're using urlTransform
to store and fetch
10 | the current language ID in/from the url. We could also do this with a
11 | dynamic folder in the root, eg. src/pages/[language]
.
12 |
13 |
14 | How localized data should be stored and resolved is entirely up to you.
15 | For simplicity, we've kept it all in one file.
16 |
17 |
18 | Click the language dropdown to change the language of this page.
19 |
20 | {:else if lang === 'de'}
21 | Internationalisierung
22 |
23 | In diesem Beispiel verwenden wir urlTransform
, um die
24 | aktuelle Sprach-ID in / aus der URL zu speichern und abzurufen. Wir
25 | könnten dies auch mit einem dynamischen Ordner im Stammverzeichnis tun,
26 | z.
27 | src/pages/[language]
.
28 |
29 |
30 | Wie lokalisierte Daten gespeichert und aufgelöst werden sollen, liegt
31 | ganz bei Ihnen. Der Einfachheit halber haben wir alles in einer Datei
32 | gespeichert.
33 |
34 |
35 | Klicken Sie auf die Sprach-Dropdown-Liste, um die Sprache dieser Seite
36 | zu ändern.
37 |
38 | {:else if lang === 'fr'}
39 | Internationalisation
40 |
41 | Dans cet exemple, nous utilisons urlTransform
pour stocker
42 | et récupérer l'ID de langue actuel dans / depuis l'url. Nous pourrions
43 | également le faire avec un dossier dynamique à la racine, par exemple.
44 | src/pages/[language]
.
45 |
46 |
47 | La manière dont les données localisées doivent être stockées et résolues
48 | dépend entièrement de vous. Pour plus de simplicité, nous avons tout
49 | gardé en un déposer.
50 |
51 |
52 | Cliquez sur la liste déroulante des langues pour changer la langue de
53 | cette page.
54 |
55 | {/if}
56 |
57 |
63 |
--------------------------------------------------------------------------------
/fragments/i18n/template/test/common/i18n.test.js:
--------------------------------------------------------------------------------
1 | /**
2 | * These tests require ava and playwright
3 | * To install:
4 | * npm install --save-dev ava playwright
5 | */
6 |
7 | const test = require('ava')
8 | const { pageMacro } = require('../lib')
9 |
10 | test('default page shows English content', pageMacro, async (t, page) => {
11 | await page.goto('http://localhost:5000/guide/i18n')
12 | t.assert(await page.waitForSelector('"Internationalization"'))
13 | })
14 |
15 | test('German page shows German content', pageMacro, async (t, page) => {
16 | await page.goto('http://localhost:5000/de/guide/i18n')
17 | t.assert(await page.waitForSelector('"Internationalisierung"'))
18 | })
19 |
20 | test('French page shows French content', pageMacro, async (t, page) => {
21 | await page.goto('http://localhost:5000/fr/guide/i18n')
22 | t.assert(await page.waitForSelector('"Internationalisation"'))
23 | })
24 |
--------------------------------------------------------------------------------
/fragments/markdown/blueprint.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | default: true,
3 | type: 'feature',
4 | imports: {
5 | mdsvex: ['mdsvex', 'mdsvex'],
6 | slug: ['remark-slug'],
7 | },
8 | configs: ({ getConfigString, $require }) => ({
9 | options: { base: { useMarkdown: true } },
10 | packagejson: require('./package.json'),
11 | svelte: {
12 | extensions: ["'.md'", "'.svelte'"],
13 | preprocess: [$require('mdsvex')(getConfigString('mdsvex'))],
14 | },
15 | mdsvex: {
16 | remarkPlugins: [$require('slug')],
17 | layout: {
18 | blog: "'src/components/Card.svelte'",
19 | },
20 | extension: "'md'",
21 | },
22 | }),
23 | }
24 |
--------------------------------------------------------------------------------
/fragments/markdown/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "devDependencies": {
3 | "mdsvex": "^0.8.9",
4 | "remark-slug": "^6.0.0",
5 | "routify-plugin-frontmatter": "^1.0.1"
6 | },
7 | "routify": {
8 | "extensions": [
9 | "md",
10 | "svelte"
11 | ],
12 | "plugins": {
13 | "routify-plugin-frontmatter": {}
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/fragments/markdown/template/src/components/Card.svelte:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/fragments/markdown/template/src/pages/guide/markdown.md:
--------------------------------------------------------------------------------
1 | ### Markdown
2 |
3 | Markdown includes `mdsvex`, `remark-slug` and `routify-plugin-frontmatter`
4 |
--------------------------------------------------------------------------------
/fragments/markdown/template/test/common/mdsvex.test.js:
--------------------------------------------------------------------------------
1 | /**
2 | * These tests require ava and playwright
3 | * To install:
4 | * npm install --save-dev ava playwright
5 | */
6 |
7 | const test = require('ava')
8 | const { pageMacro } = require('../lib')
9 |
10 | test('can view a markdown page', pageMacro, async (t, page) => {
11 | await page.goto('http://localhost:5000/guide/markdown')
12 | // await new Promise(resolve => setTimeout(resolve, 100))
13 | t.assert(await page.waitForSelector('"Markdown"'))
14 | })
15 |
--------------------------------------------------------------------------------
/fragments/milligram/blueprint.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | type: 'feature',
3 | default: true,
4 | }
5 |
--------------------------------------------------------------------------------
/fragments/milligram/template/src/pages/guide/milligram.svelte:
--------------------------------------------------------------------------------
1 | Milligram.css
2 |
3 | Milligram is a lightweight css framework.
4 |
5 | It's the default CSS framework for our examples and templates since it
6 | doesn't pollute the HTML with proprietary classes or contrived structures.
7 |
8 | To remove it, simply delete it from __app.html
9 | For more info: milligram
10 |
--------------------------------------------------------------------------------
/fragments/milligram/template/static/__app.html.fragment.js:
--------------------------------------------------------------------------------
1 | module.exports.patch = ({ placeholders }) => {
2 | placeholders.head.push(`
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | `)
17 | }
18 |
--------------------------------------------------------------------------------
/fragments/mobile/blueprint.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | type: 'template',
3 | dependencies: ['milligram', 'content'],
4 | }
5 |
--------------------------------------------------------------------------------
/fragments/mobile/template/src/components/Decorator.svelte:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/fragments/mobile/template/src/components/navigation/Navbar.svelte:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
8 | {#each $layout.children as { path, title }}
9 | {title}
10 | {/each}
11 |
12 |
13 |
--------------------------------------------------------------------------------
/fragments/mobile/template/src/components/navigation/Overlay.svelte:
--------------------------------------------------------------------------------
1 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/fragments/mobile/template/src/pages/_layout.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 | __NAVIGATION__ __HEADER__
10 |
11 | __HTML__
12 |
15 |
16 |
17 |
18 |
19 | __FOOTER__
20 |
21 |
22 |
23 |
26 |
--------------------------------------------------------------------------------
/fragments/mobile/template/src/pages/guide/mobile.svelte:
--------------------------------------------------------------------------------
1 | Mobile template
2 |
--------------------------------------------------------------------------------
/fragments/mobile/template/src/pages/home/_layout.svelte:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/fragments/mobile/template/src/pages/home/index.svelte:
--------------------------------------------------------------------------------
1 |
2 |
home
3 |
4 |
--------------------------------------------------------------------------------
/fragments/mobile/template/src/pages/index.svelte:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/fragments/mobile/template/static/__app.html.fragment.js:
--------------------------------------------------------------------------------
1 | module.exports.patch = ({ placeholders }) => {
2 | placeholders.head.push(' ')
3 | }
4 |
--------------------------------------------------------------------------------
/fragments/mobile/template/static/mobile.css:
--------------------------------------------------------------------------------
1 | #app {
2 | display: flex;
3 | flex-direction: column;
4 | height: 100vh;
5 | }
6 | main {
7 | flex-grow: 1;
8 | overflow: hidden;
9 | }
10 | .node_posts__layout {
11 | background: #0bf5cc;
12 | }
13 | .node_guide__layout {
14 | background: #a1fac3;
15 | }
16 | .node_home__layout {
17 | background: #88f0d0;
18 | }
19 | .node_introduction__layout {
20 | background: #7fc5bb;
21 | }
22 | .posts .post {
23 | background: white;
24 | }
25 | .transition {
26 | overflow: auto;
27 | }
28 | .container {
29 | padding-top: 32px;
30 | padding-bottom: 32px;
31 | }
32 |
33 | /* .transition[style^=' animation'] {
34 | overflow: hidden;
35 | } */
36 |
37 | footer nav {
38 | text-transform: uppercase;
39 | background: white;
40 | display: flex;
41 | box-shadow: 0 6px 20px 0 rgba(0, 0, 0, 0.19);
42 | justify-content: space-evenly;
43 | }
44 |
45 | footer a {
46 | font-size: 14px;
47 | color: #aaa;
48 | text-transform: uppercase;
49 | flex-grow: 1;
50 | text-align: center;
51 | line-height: 64px;
52 | }
53 |
54 | footer .overlay {
55 | transition: all 0.3s;
56 | position: fixed;
57 | border-bottom: 3px solid #a1fac3;
58 | pointer-events: none;
59 | }
60 |
--------------------------------------------------------------------------------
/fragments/navigation/blueprint.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | default: true,
3 | type: 'template',
4 | }
5 |
--------------------------------------------------------------------------------
/fragments/navigation/template/src/components/Navbar/Bar.svelte:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
9 | My App
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/fragments/navigation/template/src/components/Navbar/Navigation.svelte:
--------------------------------------------------------------------------------
1 |
12 |
23 |
24 |
25 | {#each items as { path, title, children, ...rest }}
26 |
27 |
28 | {title}
29 |
30 |
31 | {#if items && _depth < maxDepth && shouldExplode(path)}
32 |
33 | {/if}
34 |
35 | {/each}
36 |
37 |
38 |
55 |
--------------------------------------------------------------------------------
/fragments/navigation/template/src/pages/_layout.svelte:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
8 | __NAVIGATION__
9 |
10 | __NAVIGATION_RIGHT__
11 |
12 |
13 |
14 |
15 | __HTML__
16 |
17 |
18 |
19 |
20 |
21 |
22 |
39 |
--------------------------------------------------------------------------------
/fragments/navigation/template/test/common/navigation.test.js:
--------------------------------------------------------------------------------
1 | /**
2 | * These tests require ava and playwright
3 | * To install:
4 | * npm install --save-dev ava playwright
5 | */
6 |
7 | const test = require('ava')
8 | const { pageMacro } = require('../pageMacro')
9 |
10 | test('can navigate', pageMacro, async (t, page) => {
11 | await page.goto('http://localhost:5000/')
12 | t.assert(await page.waitForSelector('nav'))
13 |
14 | await page.click('"posts"')
15 | t.assert(await page.waitForSelector('"something about html"'))
16 | })
17 |
--------------------------------------------------------------------------------
/fragments/nollup/blueprint.js:
--------------------------------------------------------------------------------
1 | // @ts-check
2 |
3 | /** @type {import('canvasit')['Blueprint']} */
4 | module.exports = {
5 | name: 'rollup + nollup',
6 | type: 'bundler',
7 | dependencies: ['rollup'],
8 | imports: {
9 | assetsDir: ['./package.json', 'appConfig', 'assetsDir'],
10 | buildDir: ['./package.json', 'appConfig', 'buildDir'],
11 | distDir: ['./package.json', 'appConfig', 'distDir'],
12 | port: ['./package.json', 'appConfig', 'port'],
13 | template: ['./package.json', 'appConfig', 'template'],
14 | relative: ['path', 'relative'],
15 | svelte: ['rollup-plugin-svelte-hot'],
16 | Hmr: ['rollup-plugin-hot'],
17 | },
18 | configs: ({ $require }) => {
19 | return {
20 | nollup: {
21 | hot: 'true',
22 | contentBase: $require('assetsDir') + ', // static',
23 | publicPath:
24 | $require('relative')(
25 | $require('distDir') + ', ' + $require('buildDir'),
26 | ) + ', // build',
27 | historyApiFallback:
28 | $require('relative')(
29 | $require('assetsDir') + ', ' + $require('template'),
30 | ) + ', // __app.html',
31 | port: $require('port'),
32 | },
33 | rollup: {
34 | plugins: [
35 | `!production && isNollup && ${$require(
36 | 'Hmr',
37 | )}({ inMemory: true, public: ${$require(
38 | 'assetsDir',
39 | )}, }), //, refresh only updated code`,
40 | '!production && !isNollup && livereload(distDir), // refresh entire window when code is updated',
41 | ],
42 | watch: {
43 | clearScreen: 'false',
44 | buildDelay: '100',
45 | },
46 | },
47 | packagejson: require('./template/package.json'),
48 | }
49 | },
50 | hooks: {
51 | afterPatch: ({ transform }) => {
52 | // remove unconditional livereload. We only want it when nollup isn't running
53 | transform('rollup.config.js', str =>
54 | str.replace(/.*'!production && livereload\\(distDir\\)'.*/, ''),
55 | )
56 | },
57 | },
58 | }
59 |
--------------------------------------------------------------------------------
/fragments/nollup/template/.nolluprc.js:
--------------------------------------------------------------------------------
1 | __IMPORTS__
2 |
3 | __CONSTANTS__
4 |
5 | module.exports = __CONFIG__
6 |
--------------------------------------------------------------------------------
/fragments/nollup/template/.nolluprc.js.fragment.js:
--------------------------------------------------------------------------------
1 | exports.patch = ({ placeholders, configs, stringify }) => {
2 | placeholders.config.push(stringify(configs.nollup))
3 | }
4 |
--------------------------------------------------------------------------------
/fragments/nollup/template/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "@comments scripts": {
3 | "dev:rollup": "develop with features like SSR and serviceworker enabled",
4 | "build:rollup": "build rollup"
5 | },
6 | "scripts": {
7 | "dev": "run-p dev:routify dev:nollup",
8 | "dev:nollup": "nollup -c",
9 | "dev:ssr": "run-p dev:routify dev:rollup serve"
10 | },
11 | "devDependencies": {
12 | "nollup": "^0.15.3"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/fragments/nollup/template/rollup.config.js.fragment.js:
--------------------------------------------------------------------------------
1 | exports.patch = ({ placeholders, configs, stringify }) => {
2 | placeholders.constants.push('const isNollup = !!process.env.NOLLUP')
3 | }
4 |
--------------------------------------------------------------------------------
/fragments/postcss/blueprint.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | type: 'feature',
3 | dependencies: ['autoPreprocess'],
4 | configs: ({ getConfig }) => ({
5 | packagejson: require('./package.json'),
6 | autoPreprocess: {
7 | postcss: getConfig('postcss'),
8 | },
9 | postcss: {
10 | plugins: [],
11 | },
12 | }),
13 | }
14 |
--------------------------------------------------------------------------------
/fragments/postcss/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "devDependencies": {
3 | "postcss": "^8.2.4"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/fragments/rollup/blueprint.js:
--------------------------------------------------------------------------------
1 | // @ts-check
2 |
3 | /** @type {import('canvasit')['Blueprint']} */
4 | module.exports = {
5 | type: 'bundler',
6 | imports: {
7 | svelte: ['rollup-plugin-svelte'],
8 | resolve: ['@rollup/plugin-node-resolve'],
9 | commonjs: ['@rollup/plugin-commonjs'],
10 | livereload: ['rollup-plugin-livereload'],
11 | terser: ['rollup-plugin-terser', 'terser'],
12 | buildDir: ['./package.json', 'appConfig', 'buildDir'],
13 | distDir: ['./package.json', 'appConfig', 'distDir'],
14 | },
15 | configs: ({ getConfigString, $require }) => ({
16 | test: {
17 | domain: "'localhost'",
18 | port: '5000',
19 | },
20 | rollupResolve: {
21 | browser: 'true',
22 | dedupe: 'importee => !!importee.match(/svelte(\\/|$)/)',
23 | },
24 | rollup: {
25 | preserveEntrySignatures: 'false',
26 | input: ['`src/main.js`'],
27 | output: {
28 | sourcemap: 'true',
29 | format: "'esm'",
30 | dir: $require('buildDir'),
31 | // for performance, disabling filename hashing in development
32 | chunkFileNames: "`[name]${production && '-[hash]' || ''}.js`",
33 | },
34 | plugins: [
35 | $require('svelte')(getConfigString('svelte')),
36 |
37 | // resolve matching modules from current working directory
38 | $require('resolve')(getConfigString('rollupResolve')),
39 | $require('commonjs')(),
40 |
41 | `production && ${$require('terser')()}`,
42 | `!production && ${$require('livereload')(
43 | $require('distDir'),
44 | )} , // refresh entire window when code is updated`,
45 | ],
46 | watch: { clearScreen: 'false' },
47 | },
48 | packagejson: require('./template/package.json'),
49 | }),
50 | }
51 |
--------------------------------------------------------------------------------
/fragments/rollup/template/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "@comments scripts": {
3 | "dev:rollup": "develop with features like SSR and serviceworker enabled",
4 | "build:rollup": "build rollup"
5 | },
6 | "scripts": {
7 | "dev:rollup": "rollup -cw",
8 | "build:rollup": "rollup -c"
9 | },
10 | "devDependencies": {
11 | "@rollup/plugin-node-resolve": "^11.2.0",
12 | "@rollup/plugin-commonjs": "^17.1.0",
13 | "rollup": "^2.41.0",
14 | "rollup-plugin-livereload": "^2.0.0",
15 | "rollup-plugin-svelte": "^6.1.0",
16 | "rollup-plugin-hot": "^0.1.1",
17 | "rollup-plugin-svelte-hot": "^0.13.0",
18 | "rollup-plugin-terser": "^7.0.2"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/fragments/rollup/template/rollup.config.js:
--------------------------------------------------------------------------------
1 | import { removeSync } from 'fs-extra'
2 | __IMPORTS__
3 |
4 | __CONSTANTS__
5 |
6 | __LOGIC__
7 |
8 | __FUNCTIONS__
9 |
10 | export default __CONFIG__
11 |
12 | __ESM__
13 |
--------------------------------------------------------------------------------
/fragments/rollup/template/rollup.config.js.fragment.js:
--------------------------------------------------------------------------------
1 | exports.patch = ({ placeholders, configs, stringify, $require }) => {
2 | placeholders.config.push(stringify(configs.rollup))
3 | placeholders.logic.push(`// clear previous builds`)
4 | placeholders.logic.push(`removeSync(${$require('distDir')})`)
5 | placeholders.constants.push(
6 | `const production = process.env['NODE_ENV'] === 'production'`,
7 | )
8 | }
9 |
--------------------------------------------------------------------------------
/fragments/rollup/template/src/pages/guide/rollup.svelte:
--------------------------------------------------------------------------------
1 | Rollup
2 |
3 | Rollup is the default Svelte/Routify bundler.
4 |
5 | It doesn't offer "no-bundling", but thanks to libraries like nollup
it can offer the same lightning fast rebuilds.
7 |
8 |
9 | Rollup runs automatically with npm run dev
and
10 | npm run build
.
11 |
12 |
13 | To run Rollup in isolation, run npm run dev:rollup
or
14 | npm run build:rollup
15 |
16 |
--------------------------------------------------------------------------------
/fragments/rollup/template/static/__app.html.fragment.js:
--------------------------------------------------------------------------------
1 | exports.patch = ({ placeholders, configs, stringify }) => {
2 | placeholders.head.push(' ')
3 | placeholders.head.push(
4 | '',
5 | )
6 | }
7 |
--------------------------------------------------------------------------------
/fragments/scroll-navigation/blueprint.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | type: 'template',
3 | }
4 |
--------------------------------------------------------------------------------
/fragments/scroll-navigation/template/src/pages/_layout.svelte:
--------------------------------------------------------------------------------
1 |
26 |
27 | __HTML__
28 |
29 |
30 |
31 |
32 |
33 |
34 | __NAVIGATIONFIRST__ __NAVIGATION__
35 |
36 | {#each $components.find(n => n.path === '/frontpage').children as node}
37 |
{node.title}
38 | {/each}
39 | |
40 | {#each $layout.children as node}
41 |
{node.title}
42 | {/each}
43 | __NAVIGATIONMIDDLE__
44 |
45 | __NAVIGATIONLAST__
46 |
47 |
48 |
71 |
--------------------------------------------------------------------------------
/fragments/scroll-navigation/template/src/pages/frontpage/_layout.svelte:
--------------------------------------------------------------------------------
1 |
2 |
3 | Go to the front page to see this page
4 |
5 |
6 |
--------------------------------------------------------------------------------
/fragments/scroll-navigation/template/src/pages/frontpage/contact.svelte:
--------------------------------------------------------------------------------
1 | Contact
2 |
3 |
4 |
--------------------------------------------------------------------------------
/fragments/scroll-navigation/template/src/pages/frontpage/features.svelte:
--------------------------------------------------------------------------------
1 |
2 | Features
3 |
--------------------------------------------------------------------------------
/fragments/scroll-navigation/template/src/pages/frontpage/home.svelte:
--------------------------------------------------------------------------------
1 | Home
2 |
3 |
4 |
--------------------------------------------------------------------------------
/fragments/scroll-navigation/template/src/pages/frontpage/story.svelte:
--------------------------------------------------------------------------------
1 | Story
2 |
3 |
4 |
--------------------------------------------------------------------------------
/fragments/scroll-navigation/template/src/pages/guide/single-page.svelte:
--------------------------------------------------------------------------------
1 | Single page navigation
2 |
3 | Navigational scrolling - not to be confused with single page application.
4 |
5 |
6 | All pages in `/frontpage` are plain files, but we could also have used
7 | folders.
8 |
9 |
--------------------------------------------------------------------------------
/fragments/scroll-navigation/template/src/pages/index.svelte:
--------------------------------------------------------------------------------
1 |
20 |
21 |
22 |
23 | {#each nodes as node, index}
24 |
27 | {#await node.componentWithIndex then [Cmp, IndexCmp]}
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 | {/await}
39 | {/each}
40 |
41 |
42 |
52 |
--------------------------------------------------------------------------------
/fragments/serviceworker/blueprint.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | type: 'feature',
3 | configs: ({ getConfig, stringify }) => ({
4 | packagejson: {
5 | devDependencies: {
6 | 'workbox-cli': '^6.1.0',
7 | esbuild: '^0.8.42',
8 | },
9 | scripts: {
10 | 'build:pwa-bundle':
11 | 'esbuild --bundle src/sw.js --outfile=public/sw.generated.js --define:process.env.NODE_ENV="\'production\'"',
12 | 'build:pwa-inject': 'workbox injectManifest',
13 | },
14 | },
15 | }),
16 | hooks: {
17 | afterPatch: ctx => {
18 | ctx.writeTo(
19 | 'package.json',
20 | JSON.stringify(ctx.configs.packagejson, null, 2),
21 | )
22 | },
23 | },
24 | }
25 |
--------------------------------------------------------------------------------
/fragments/serviceworker/template/src/App.svelte.fragment.js:
--------------------------------------------------------------------------------
1 | module.exports.patch = ({ placeholders }) => {
2 | placeholders.imports.push(
3 | 'import Serviceworker from "./components/Serviceworker.svelte"',
4 | )
5 | placeholders.html.push(' ')
6 | }
7 |
--------------------------------------------------------------------------------
/fragments/serviceworker/template/src/components/Serviceworker.svelte:
--------------------------------------------------------------------------------
1 |
19 |
--------------------------------------------------------------------------------
/fragments/serviceworker/template/src/pages/guide/serviceworker.md:
--------------------------------------------------------------------------------
1 | ### Serviceworker
2 |
3 | The serviceworker prodivides offline availability and precaching
4 |
--------------------------------------------------------------------------------
/fragments/serviceworker/template/src/sw.js:
--------------------------------------------------------------------------------
1 | // @ts-check
2 |
3 | import {
4 | registerRoute,
5 | setDefaultHandler,
6 | setCatchHandler,
7 | } from 'workbox-routing'
8 | import {
9 | CacheFirst,
10 | NetworkFirst,
11 | StaleWhileRevalidate,
12 | } from 'workbox-strategies'
13 | import { skipWaiting, clientsClaim } from 'workbox-core'
14 | import { precacheAndRoute, matchPrecache } from 'workbox-precaching'
15 | import { ExpirationPlugin } from 'workbox-expiration'
16 | import { RoutifyPlugin, freshCacheData } from '@roxi/routify/workbox-plugin'
17 |
18 | /**********
19 | * CONFIG *
20 | **********/
21 |
22 | const entrypointUrl = '__app.html' // entrypoint
23 | const fallbackImage = '404.svg'
24 | const files = self.__WB_MANIFEST // files matching globDirectory and globPattern in rollup.config.js
25 |
26 | const externalAssetsConfig = () => ({
27 | cacheName: 'external',
28 | plugins: [
29 | RoutifyPlugin({
30 | validFor: 60, // cache is considered fresh for n seconds.
31 | }),
32 | new ExpirationPlugin({
33 | maxEntries: 50, // last used entries will be purged when we hit this limit
34 | purgeOnQuotaError: true, // purge external assets on quota error
35 | }),
36 | ],
37 | })
38 |
39 | /**************
40 | * INITIALIZE *
41 | **************/
42 |
43 | /**
44 | * precache all files
45 | * remember to precache __app.html and 404.svg if caching of all files is disabled
46 | */
47 | precacheAndRoute(files)
48 |
49 | /** precache only fallback files */
50 | // precacheAndRoute(files.filter(file =>
51 | // ['__app.html', '404.svg']
52 | // .includes(file.url)
53 | // ))
54 |
55 | skipWaiting() // auto update service workers across all tabs when new release is available
56 | clientsClaim() // take control of client without having to wait for refresh
57 |
58 | /**
59 | * manually upgrade service worker by sending a SKIP_WAITING message.
60 | * (remember to disable skipWaiting() above)
61 | */
62 | // addEventListener('message', event => { if (event.data && event.data.type === 'SKIP_WAITING') skipWaiting(); });
63 |
64 | /**********
65 | * ROUTES *
66 | **********/
67 |
68 | // serve local pages from the SPA entry point (__app.html)
69 | registerRoute(isLocalPage, matchPrecache(entrypointUrl))
70 |
71 | // serve local assets from cache first
72 | registerRoute(isLocalAsset, new CacheFirst())
73 |
74 | // serve external assets from cache if they're fresh
75 | registerRoute(hasFreshCache, new CacheFirst(externalAssetsConfig()))
76 |
77 | // serve external pages and assets
78 | setDefaultHandler(new NetworkFirst(externalAssetsConfig()))
79 |
80 | // serve a fallback for 404s if possible or respond with an error
81 | setCatchHandler(async ({ event }) => {
82 | switch (event.request.destination) {
83 | case 'document':
84 | return await matchPrecache(entrypointUrl)
85 | case 'image':
86 | return await matchPrecache(fallbackImage)
87 | default:
88 | return Response.error()
89 | }
90 | })
91 |
92 | /**********
93 | * CONDITIONS *
94 | **********/
95 |
96 | function isLocalAsset({ url, request }) {
97 | return url.host === self.location.host && request.destination != 'document'
98 | }
99 | function isLocalPage({ url, request }) {
100 | return url.host === self.location.host && request.destination === 'document'
101 | }
102 | function hasFreshCache(event) {
103 | return !!freshCacheData(event)
104 | }
105 |
106 | /** Example condition */
107 | function hasWitheringCache(event) {
108 | const cache = freshCacheData(event)
109 | if (cache) {
110 | const { cachedAt, validFor, validLeft, validUntil } = cache
111 | // return true if half the fresh time has passed
112 | return validFor / 2 > validFor - validLeft
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/fragments/serviceworker/template/static/images/touch-icons/logo-192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/roxiness/stackmix/7f0f0f2c4d2b57fa1ee37536c0f07f4e2b9d3beb/fragments/serviceworker/template/static/images/touch-icons/logo-192.png
--------------------------------------------------------------------------------
/fragments/serviceworker/template/static/images/touch-icons/logo-800.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/roxiness/stackmix/7f0f0f2c4d2b57fa1ee37536c0f07f4e2b9d3beb/fragments/serviceworker/template/static/images/touch-icons/logo-800.png
--------------------------------------------------------------------------------
/fragments/serviceworker/template/static/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "background_color": "#ffffff",
3 | "theme_color": "#E938C2",
4 | "name": "Routify app",
5 | "short_name": "Routify app",
6 | "start_url": "/",
7 | "display": "standalone",
8 | "icons": [
9 | {
10 | "src": "/images/touch-icons/logo-192.png",
11 | "sizes": "192x192",
12 | "type": "image/png"
13 | },
14 | {
15 | "src": "/images/touch-icons/logo-800.png",
16 | "sizes": "800x800",
17 | "type": "image/png",
18 | "purpose": "maskable any"
19 | }
20 | ]
21 | }
22 |
--------------------------------------------------------------------------------
/fragments/serviceworker/template/workbox-config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | globDirectory: 'public',
3 | globPatterns: ['**/*.{js,css,svg}', '__app.html'],
4 | swSrc: `public/sw.generated.js`,
5 | swDest: `public/sw.generated.js`,
6 | maximumFileSizeToCacheInBytes: 10000000, // 10 MB,
7 | }
8 |
--------------------------------------------------------------------------------
/fragments/snowpack/blueprint.js:
--------------------------------------------------------------------------------
1 | /** @type {import('canvasit')['Blueprint']} */
2 | module.exports = {
3 | type: 'bundler',
4 | configs: () => ({
5 | packagejson: require('./package.json'),
6 | }),
7 | hooks: {
8 | afterPatch: ctx => {
9 | const parts = ctx.parseImports(ctx.stringify(ctx.configs.svelte))
10 | ctx.writeTo(
11 | 'svelte.config.js',
12 | `
13 | ${parts.imports.join('\n')}
14 | ${parts.declarations.join('\n')}
15 | const production = process.env.NODE_ENV === 'production'
16 | module.exports = ${parts.body}
17 | `,
18 | )
19 | },
20 | },
21 | }
22 |
--------------------------------------------------------------------------------
/fragments/snowpack/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "scripts": {
3 | "dev": "run-p dev:*",
4 | "dev:snowpack": "snowpack dev",
5 | "build:snowpack": "snowpack build"
6 | },
7 | "devDependencies": {
8 | "@snowpack/plugin-dotenv": "^2.0.5",
9 | "@snowpack/plugin-svelte": "^3.5.2",
10 | "snowpack": "^3.0.13"
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/fragments/snowpack/template/snowpack.config.js:
--------------------------------------------------------------------------------
1 | const { assetsDir, distDir } = require('./package.json').appConfig
2 |
3 | /** @type {import("snowpack").SnowpackUserConfig } */
4 | module.exports = {
5 | mount: {
6 | [assetsDir]: { url: '/', static: true },
7 | src: { url: '/build' },
8 | '.routify': { url: '/.routify' },
9 | },
10 | routes: [{ match: 'routes', src: '.*', dest: '/__app.html' }],
11 | plugins: ['@snowpack/plugin-svelte', '@snowpack/plugin-dotenv'],
12 | packageOptions: {
13 | knownEntrypoints: [
14 | /* ... */
15 | ],
16 | /* ... */
17 | },
18 | devOptions: {
19 | open: 'none',
20 | port: 5000,
21 | /* ... */
22 | },
23 | buildOptions: {
24 | out: distDir,
25 | /* ... */
26 | },
27 | alias: {
28 | /* ... */
29 | },
30 | }
31 |
--------------------------------------------------------------------------------
/fragments/snowpack/template/src/pages/guide/snowpack.md:
--------------------------------------------------------------------------------
1 | #### Snowpack
2 |
3 | Snowpack is a lightning-fast frontend build tool, designed for the modern web. It is an alternative to heavier, more complex bundlers like webpack or Parcel in your development workflow. Snowpack leverages JavaScript's native module system (known as ESM) to avoid unnecessary work and stay fast no matter how big your project grows.
4 |
--------------------------------------------------------------------------------
/fragments/snowpack/template/static/__app.html.fragment.js:
--------------------------------------------------------------------------------
1 | exports.patch = ({ placeholders, configs, stringify }) => {
2 | placeholders.head.push(' ')
3 | placeholders.head.push(
4 | '',
5 | )
6 | }
7 |
--------------------------------------------------------------------------------
/fragments/spank/blueprint.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | name: 'Spank (Static Site Generator)',
3 | type: 'feature',
4 | configs: ({ getConfig, stringify }) => ({
5 | packagejson: require('./package.json'),
6 | }),
7 | }
8 |
--------------------------------------------------------------------------------
/fragments/spank/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "scripts": {
3 | "build:static": "spank"
4 | },
5 | "devDependencies": {
6 | "spank": "^1.5.0"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/fragments/spank/template/src/pages/guide/spank.md:
--------------------------------------------------------------------------------
1 | ### Spank (Static Site Generation)
2 |
3 | `spank` crawls your site and produces a corresponding HTML file for each page it encounters.
4 |
5 | [Official documentation](https://github.com/roxiness/spank)
6 |
--------------------------------------------------------------------------------
/fragments/spank/template/test/build/spank.test.js:
--------------------------------------------------------------------------------
1 | /**
2 | * These tests require ava and playwright
3 | * To install:
4 | * npm install --save-dev ava playwright
5 | */
6 |
7 | const { existsSync, readFileSync } = require('fs')
8 | const test = require('ava')
9 | const { resolve, pkgJson } = require('../lib')
10 | const { distDir } = pkgJson.appConfig
11 |
12 | test('index.html was prerenderd', async (t, page) => {
13 | const indexHtml = resolve(distDir, 'index.html')
14 | t.assert(existsSync(indexHtml), 'index.html should exist')
15 |
16 | const content = readFileSync(indexHtml, 'utf-8')
17 | t.regex(
18 | content,
19 | //,
20 | "index.html's body should have prerendered content",
21 | )
22 | })
23 |
--------------------------------------------------------------------------------
/fragments/tailwindcss/blueprint.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | type: 'feature',
3 | dependencies: ['postcss'],
4 | imports: {
5 | tailwind: ['tailwindcss'],
6 | postcssImport: ['postcss-import'],
7 | },
8 | configs: ({ getConfigString, getConfig, $require }) => ({
9 | packagejson: require('./package.json'),
10 | autoPreprocess: {
11 | postcss: getConfig('postcss'),
12 | },
13 |
14 | tailwindcss: {
15 | mode: "'jit'",
16 | darkMode: "'class'",
17 | future: {
18 | removeDeprecatedGapUtilities: 'true',
19 | purgeLayersByDefault: 'true',
20 | },
21 | plugins: [],
22 | purge: {
23 | content: ["'./src/**/*.svelte'"],
24 | enabled: 'production',
25 | },
26 | },
27 | postcss: {
28 | plugins: [
29 | $require('tailwind')(getConfigString('tailwindcss')),
30 | $require('postcssImport'),
31 | ],
32 | },
33 | }),
34 | hooks: {
35 | beforeConfig(ctx) {
36 | ctx.prompt
37 | },
38 | },
39 | }
40 |
--------------------------------------------------------------------------------
/fragments/tailwindcss/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "devDependencies": {
3 | "autoprefixer": "^10.2.3",
4 | "cssnano": "^4.1.10",
5 | "postcss-import": "^13.0.0",
6 | "tailwindcss": "^2.1.1"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/fragments/tailwindcss/template/.browserslistrc:
--------------------------------------------------------------------------------
1 | last 8 version
--------------------------------------------------------------------------------
/fragments/tailwindcss/template/src/App.svelte.fragment.js:
--------------------------------------------------------------------------------
1 | exports.patch = ({ placeholders, configs, stringify }) => {
2 | placeholders.style.push(`
3 | @tailwind base;
4 | @tailwind components;
5 | @tailwind utilities;`)
6 | }
7 |
--------------------------------------------------------------------------------
/fragments/tailwindcss/template/src/pages/guide/tailwind.svelte:
--------------------------------------------------------------------------------
1 | Tailwindcss
2 |
3 |
4 |
5 | It's running with tailwind!
6 |
7 |
8 |
9 | Instructions to follow...
10 |
--------------------------------------------------------------------------------
/fragments/vercel/blueprint.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | type: 'feature',
3 | }
4 |
--------------------------------------------------------------------------------
/fragments/vercel/template/api/vercel-ssr/build.js:
--------------------------------------------------------------------------------
1 | const { resolve } = require('path')
2 | const { existsSync } = require('fs')
3 | const { execSync } = require('child_process')
4 | const { rollup } = require('rollup')
5 |
6 | const shouldBuildSpa =
7 | process.env.NOW_GITHUB_DEPLOYMENT || process.env.NOW_BUILDER
8 | const script = resolve(__dirname, '../../dist/build/main.js')
9 | const bundlePath = resolve(__dirname, '../../dist/build/bundle.js')
10 |
11 | build()
12 |
13 | async function build() {
14 | if (shouldBuildSpa)
15 | execSync('npm install && npm run build:app', {
16 | cwd: resolve('..', '..'),
17 | stdio: 'inherit',
18 | })
19 | else await waitForAppToExist()
20 |
21 | buildSSRBundle()
22 | }
23 |
24 | async function waitForAppToExist() {
25 | while (!existsSync(script)) {
26 | console.log(`checking if "${script}" exists`)
27 | await new Promise(r => setTimeout(r, 2000))
28 | }
29 | console.log(`found "${script}"`)
30 | }
31 |
32 | async function buildSSRBundle() {
33 | const bundle = await rollup({
34 | input: script,
35 | inlineDynamicImports: true,
36 | })
37 | await bundle.write({ format: 'umd', file: bundlePath, name: 'roxi-ssr' })
38 | }
39 |
--------------------------------------------------------------------------------
/fragments/vercel/template/api/vercel-ssr/index.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs')
2 | const { tossr } = require('tossr')
3 |
4 | const script = fs.readFileSync(
5 | require.resolve('../../dist/build/bundle.js'),
6 | 'utf8',
7 | )
8 | const template = fs.readFileSync(
9 | require.resolve('../../dist/__app.html'),
10 | 'utf8',
11 | )
12 |
13 | module.exports = async (req, res) => {
14 | const html = await tossr(template, script, req.url, {})
15 | res.send(html + '\n')
16 | }
17 |
--------------------------------------------------------------------------------
/fragments/vercel/template/api/vercel-ssr/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "scripts": {
3 | "vercel-build": "node ./build.js"
4 | },
5 | "devDependencies": {
6 | "rollup": "^2.28.2"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/fragments/vercel/template/vercel.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 2,
3 | "functions": {
4 | "api/vercel-ssr/index.js": {
5 | "includeFiles": "dist/**"
6 | }
7 | },
8 | "routes": [
9 | {
10 | "handle": "filesystem"
11 | },
12 | {
13 | "src": "/.*",
14 | "dest": "/api/vercel-ssr/index.js"
15 | }
16 | ]
17 | }
18 |
--------------------------------------------------------------------------------
/fragments/vite/blueprint.js:
--------------------------------------------------------------------------------
1 | // @ts-check
2 |
3 | /** @type {import('canvasit')['Blueprint']} */
4 | module.exports = {
5 | default: true,
6 | imports: {
7 | svelte: ['@sveltejs/vite-plugin-svelte', 'svelte'],
8 | resolve: ['path', 'resolve'],
9 | port: ['./package.json', 'appConfig', 'port'],
10 | viteMainJs: ['vite-main-js'],
11 | },
12 | type: 'bundler',
13 | configs: ({ getConfigString, $require }) => ({
14 | packagejson: require('./package.json'),
15 | svelte: {
16 | emitCss: 'true',
17 | hot: '!production',
18 | },
19 | vite: {
20 | server: {
21 | port: $require('port'),
22 | },
23 | build: {
24 | // remove this if you're not using tossr, spassr or spank
25 | polyfillModulePreload: 'false',
26 | cssCodeSplit: 'false'
27 | },
28 | optimizeDeps: {
29 | exclude: ["'@roxi/routify'"],
30 | },
31 | resolve: {
32 | dedupe: ["'@roxi/routify'"],
33 | },
34 | plugins: [
35 | $require('viteMainJs')(),
36 | $require('svelte')(getConfigString('svelte')),
37 | ],
38 | },
39 | }),
40 | hooks: {
41 | afterConfig: ctx => {
42 | delete ctx.configs.packagejson.spassr
43 | delete ctx.configs.packagejson.spank
44 | },
45 | afterPatch: ctx => {
46 | ctx.moveFile('static/__app.html', 'index.html')
47 | ctx.moveFile('static', 'public')
48 | const sviteParts = ctx.parseImports(ctx.stringify(ctx.configs.vite))
49 | ctx.writeTo(
50 | 'vite.config.js',
51 | `
52 | ${sviteParts.imports.join('\n')}
53 | ${sviteParts.declarations.join('\n')}
54 | const production = process.env.NODE_ENV === 'production'
55 | module.exports = ${sviteParts.body}
56 | `,
57 | )
58 | },
59 | },
60 | }
61 |
--------------------------------------------------------------------------------
/fragments/vite/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "scripts": {
3 | "dev": "run-p dev:*",
4 | "dev:vite": "vite",
5 | "build:vite": "vite build",
6 | "serve:vite": "vite preview"
7 | },
8 | "devDependencies": {
9 | "@sveltejs/vite-plugin-svelte": "^1.0.0-next.10",
10 | "npm-run-all": "^4.1.5",
11 | "svelte-hmr": "^0.14.4",
12 | "vite": "^2.3.7",
13 | "vite-main-js": "^0.0.1"
14 | },
15 | "appConfig": {
16 | "script": "dist/main.js",
17 | "buildDir": "dist/assets",
18 | "assetsDir": "public",
19 | "template": "dist/index.html"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/fragments/vite/template/src/pages/guide/vite.md:
--------------------------------------------------------------------------------
1 | ### Vite
2 |
3 | Lightning fast (no)bundler. Server for modules in development, uses Rollup for production builds. Rollup plugins compatible.
4 |
--------------------------------------------------------------------------------
/fragments/vite/template/static/__app.html.fragment.js:
--------------------------------------------------------------------------------
1 | exports.patch = ({ placeholders, configs, stringify }) => {
2 | placeholders.body.push('')
3 | }
4 |
--------------------------------------------------------------------------------
/fragments/windicss/blueprint.js:
--------------------------------------------------------------------------------
1 | // @ts-check
2 |
3 | /** @type {import('canvasit')['Blueprint']} */
4 | module.exports = {
5 | type: 'feature',
6 | imports: {
7 | svelteWindicss: ['svelte-windicss-preprocess', 'preprocess'],
8 | },
9 | configs: ({ getConfigString, $require }) => ({
10 | windicss: {
11 | compile: 'false',
12 | prefix: '"windi-"',
13 | globalPreflight: 'true',
14 | globalUtility: 'true',
15 | },
16 | svelte: {
17 | preprocess: [
18 | $require('svelteWindicss')(getConfigString('windicss')),
19 | ],
20 | },
21 | packagejson: require('./package.json'),
22 | }),
23 | }
24 |
--------------------------------------------------------------------------------
/fragments/windicss/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "devDependencies": {
3 | "svelte-windicss-preprocess": "^3.0.0-beta.8"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/fragments/windicss/template/src/pages/guide/windicss.svelte:
--------------------------------------------------------------------------------
1 | Windicss
2 | Check my style...
3 |
4 |
9 |
--------------------------------------------------------------------------------
/lib/cli.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | const { program } = require('commander')
4 | const { merge } = require('canvasit')
5 | const prompts = require('prompts')
6 | const { resolve } = require('path')
7 | const { promptFragments } = require('./prompts')
8 |
9 | const commaSeparatedList = v => v.split(/[ ,]/)
10 |
11 | program
12 | .arguments('[name]')
13 | .option('-f, --fragments ', 'comma separated list of fragments to be used', commaSeparatedList)
14 | .action(async (name, options, command) => {
15 | let { fragments } = options
16 |
17 | fragments = fragments || await promptFragments()
18 |
19 | name = name
20 | || (await prompts({
21 | type: 'text',
22 | message: 'type the folder where you would like to install',
23 | name: 'name',
24 | initial: ['routify', ...fragments, 'app'].join('-')
25 | })).name
26 |
27 | await merge(
28 | fragments,
29 | resolve(process.cwd(), name),
30 | {
31 | basepath: resolve(__dirname, '..', 'fragments'),
32 | ignore: [
33 | "node_modules",
34 | "dist",
35 | ".routify"
36 | ],
37 | include: [
38 | "base"
39 | ],
40 | prettierPlugins: [require.resolve('prettier-plugin-svelte')]
41 | }
42 | )
43 | })
44 | .parse()
45 |
--------------------------------------------------------------------------------
/lib/generate-combos.js:
--------------------------------------------------------------------------------
1 |
2 | const { resolve } = require('path')
3 |
4 | const combinations = [
5 | 'rollup, scroll-navigation, dev, milligram',
6 | 'rollup',
7 | 'nollup',
8 | 'nollup, spank, vercel',
9 | 'nollup, docker-ssr, spank',
10 | 'nollup, tailwindcss, spank',
11 | 'snowpack',
12 | 'rollup, i18n, navigation, content, milligram, markdown, auth'
13 | ]
14 |
15 | combinations.forEach(fragments => {
16 | fragments = fragments.split(/, */)
17 | Object.keys(require.cache).forEach(key => delete require.cache[key])
18 | const dest = resolve('templates', fragments.join('-'))
19 | const { merge } = require('canvasit')
20 | merge(fragments, dest)
21 | })
--------------------------------------------------------------------------------
/lib/prompts.js:
--------------------------------------------------------------------------------
1 | const prompts = require('prompts');
2 | const { readdirSync } = require('fs');
3 | const { resolve } = require('path');
4 |
5 | const FRAGMENTS_PATH = resolve(__dirname, '..', 'fragments')
6 |
7 | const TYPES = {
8 | bundler: { message: 'Pick a bundler. Combine at your own risk.' },
9 | template: { message: 'Pick a template. Combine at your own risk.' },
10 | feature: { message: 'Pick features.' },
11 | content: { message: 'Pick content.' },
12 | }
13 |
14 | const getBlueprintFromPath = (fragment) => ({
15 | path: fragment,
16 | ...require(resolve(FRAGMENTS_PATH, fragment, 'blueprint.js'))
17 | })
18 |
19 | const groupBlueprintsByType = (types, fragment) => {
20 | if (fragment.type !== 'base') {
21 | const type = fragment.type || 'other'
22 | types[type] = types[type] || {}
23 | types[type].fragments = types[type].fragments || []
24 | types[type].fragments.push(fragment)
25 | }
26 | return types
27 | }
28 |
29 | const promptFragments = async () => {
30 | const fragmentPaths = []
31 | const fragmentByTypes = readdirSync(FRAGMENTS_PATH)
32 | .map(getBlueprintFromPath)
33 | .reduce(groupBlueprintsByType, TYPES)
34 |
35 | for (const [name, type] of Object.entries(fragmentByTypes)) {
36 | const result = await prompts({
37 | name: 'fragmentPaths',
38 | type: 'multiselect',
39 | instructions: false,
40 | message: type.message || 'Misc',
41 | choices: type.fragments.map(({ name, path, default: selected }) => ({ title: name || path, value: path, selected })),
42 | })
43 | fragmentPaths.push(...result.fragmentPaths)
44 | }
45 | return fragmentPaths
46 | }
47 |
48 | module.exports = { promptFragments }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "stackmix",
3 | "version": "0.1.8",
4 | "description": "",
5 | "main": "index.js",
6 | "bin": "lib/cli.js",
7 | "scripts": {
8 | "dev": "canvasit --watch -output output",
9 | "help": "canvasit help",
10 | "generate": "canvasit -output output",
11 | "test": "node test",
12 | "test:output:dev": "cd output && npm run test:dev",
13 | "test:output:build": "cd output && npm run test:build",
14 | "build": "node lib/generate-combos"
15 | },
16 | "author": "",
17 | "license": "ISC",
18 | "devDependencies": {
19 | "ava": "^3.15.0",
20 | "kleur": "^3.0.3",
21 | "npm": "^7.6.3",
22 | "playwright": "^1.10.0",
23 | "prettier": "^2.3.0"
24 | },
25 | "dependencies": {
26 | "canvasit": "^0.9.1",
27 | "commander": "^7.2.0",
28 | "markdown-it": "^12.0.4",
29 | "prettier-plugin-svelte": "^2.1.0",
30 | "prompts": "^2.4.0",
31 | "svelte": "^3.31.2"
32 | },
33 | "canvasit": {
34 | "include": [
35 | "base"
36 | ],
37 | "basepath": "fragments"
38 | },
39 | "prettier": {
40 | "singleQuote": true,
41 | "quoteProps": "as-needed",
42 | "trailingComma": "all",
43 | "bracketSpacing": true,
44 | "arrowParens": "avoid",
45 | "semi": false,
46 | "useTabs": false,
47 | "tabWidth": 4,
48 | "svelteSortOrder": "options-scripts-markup-styles",
49 | "svelteAllowShorthand": true,
50 | "svelteBracketNewLine": false,
51 | "svelteIndentScriptAndStyle": true
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/test/index.js:
--------------------------------------------------------------------------------
1 | const prompts = require('prompts');
2 | const { writeFileSync, existsSync, mkdirSync, rmdirSync, readdirSync, statSync } = require('fs')
3 | const { resolve } = require('path')
4 | const { spawnSync, spawn, execFileSync, execSync } = require('child_process')
5 | const kleur = require('kleur')
6 |
7 |
8 | const fragmentCombos = [
9 | 'rollup,auth,content,i18n,milligram,markdown,spank',
10 | 'vite',
11 | 'nollup',
12 | 'snowpack'
13 | ]
14 |
15 |
16 |
17 | const npm = /^win/.test(process.platform) ? 'npm.cmd' : 'npm'
18 | const npx = /^win/.test(process.platform) ? 'npx.cmd' : 'npx'
19 | const canvasit = /^win/.test(process.platform) ? 'canvasit.cmd' : 'canvasit'
20 | const stdio = 'inherit'
21 |
22 | const workspaceDir = resolve(__dirname, 'workspace')
23 | removeTemplatesFromWorkspaceDir(workspaceDir)
24 | writeFileSync(resolve(workspaceDir, 'package.json'), '{"workspaces": ["*"]}')
25 |
26 |
27 | const children = fragmentCombos.map(async combo => {
28 | const name = combo.replace(/,/g, '-')
29 | const outputDir = resolve(__dirname, 'workspace', name)
30 | const templatePkgJsonPath = resolve(outputDir, 'package.json')
31 | const child = spawn(canvasit, ['-output', outputDir, '-fragments', combo], { stdio })
32 | await new Promise(resolve => child.on('close', resolve))
33 | const pkgjson = require(templatePkgJsonPath)
34 | pkgjson.name = `test-${name}`
35 | writeFileSync(templatePkgJsonPath, JSON.stringify(pkgjson, null, 2))
36 | console.log(`[test] testing combos:`, combo)
37 | })
38 |
39 | Promise.all(children).then(() => {
40 | console.log('[test] Installing NPN dependencies')
41 | spawnSync(npm, ['install'], { cwd: workspaceDir, stdio })
42 | console.log('[test] Installed NPM dependencies')
43 | for (const combo of fragmentCombos) {
44 | const msg = kleur.bold(`| TEST: ${kleur.yellow(combo)} |`)
45 | const length = msg.length - 20
46 | console.log(`+${new Array(length).join('-')}+`)
47 | console.log(`|${new Array(length).join(' ')}|`)
48 | console.log(msg)
49 | console.log(`|${new Array(length).join(' ')}|`)
50 | console.log(`+${new Array(length).join('-')}+`)
51 | spawnSync(npm, ['test'], { cwd: resolve('test', 'workspace', combo.replace(/,/g, '-')), stdio })
52 | }
53 | })
54 |
55 |
56 | function removeTemplatesFromWorkspaceDir(dir) {
57 | if (existsSync(dir)) {
58 | readdirSync(dir)
59 | .filter(file => file !== 'node_modules')
60 | .map(file => resolve(dir, file))
61 | .filter(filepath => statSync(filepath).isDirectory())
62 | .forEach(filepath => rmdirSync(filepath, { recursive: true }))
63 | }
64 | else
65 | mkdirSync(dir)
66 | }
--------------------------------------------------------------------------------