create-ui0 0.0.0-stage → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js ADDED
@@ -0,0 +1,168 @@
1
+ import { cpSync, existsSync, mkdirSync, readdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
2
+ import path from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+ import prompts from 'prompts';
5
+ /**
6
+ * create-ui0 - scaffolds a ui0 (Vue + @ui0 packages) app.
7
+ * Usage: npm create ui0 [dir]
8
+ */
9
+ const templateDir = fileURLToPath(new URL('../template', import.meta.url));
10
+ const componentTemplateDir = fileURLToPath(new URL('../template-component', import.meta.url));
11
+ const toKebab = (s) => s
12
+ .replace(/([a-z0-9])([A-Z])/g, '$1-$2')
13
+ .replace(/[\s_]+/g, '-')
14
+ .toLowerCase();
15
+ const toPascal = (s) => s.split(/[-\s_]+/).map((w) => w[0].toUpperCase() + w.slice(1)).join('');
16
+ const toCamel = (s) => {
17
+ const p = toPascal(s);
18
+ return p[0].toLowerCase() + p.slice(1);
19
+ };
20
+ /** substitutes {%kebab%} / {%Pascal%} / {%camel%} in a template file's content */
21
+ const substitute = (content, name) => content
22
+ .replaceAll('{%kebab%}', toKebab(name))
23
+ .replaceAll('{%Pascal%}', toPascal(name))
24
+ .replaceAll('{%camel%}', toCamel(name));
25
+ /** selectable template features - each adds deps (and a demo section) */
26
+ const FEATURES = [
27
+ {
28
+ id: 'flex',
29
+ title: 'Flex layout',
30
+ deps: { '@ui0/flex': '^0.1.1' },
31
+ importLine: "import Flex from '@ui0/flex'",
32
+ section: ` <section>
33
+ <h2>Flex</h2>
34
+ <Flex items="center">
35
+ <div :class="$style.box">one</div>
36
+ <div :class="$style.box">two</div>
37
+ <div :class="$style.box">three</div>
38
+ </Flex>
39
+ </section>`
40
+ },
41
+ {
42
+ id: 'button',
43
+ title: 'Button component',
44
+ deps: { '@ui0/button': '^0.1.1' },
45
+ importLine: "import Button from '@ui0/button'",
46
+ section: ` <section>
47
+ <h2>Button</h2>
48
+ <Button>default</Button>
49
+ </section>`
50
+ },
51
+ {
52
+ id: 'icon',
53
+ title: 'Icon component + ui0 icon pack',
54
+ deps: { '@ui0/icon': '^0.1.1', '@ui0/icons': '^0.1.1' },
55
+ importLine: "import { Icon } from '@ui0/icon'",
56
+ section: ` <section>
57
+ <h2>Icon</h2>
58
+ <Icon value="ui0/heart-alt" />
59
+ </section>`
60
+ }
61
+ ];
62
+ const onCancel = () => {
63
+ console.log('aborted');
64
+ process.exit(0);
65
+ };
66
+ /**
67
+ * `create-ui0 component [Name]` - scaffolds a single component folder from
68
+ * template-component/ into the project at cwd (src/components/<kebab>/).
69
+ */
70
+ const runComponent = async () => {
71
+ const argName = process.argv[3];
72
+ const { name } = await prompts({ type: argName ? null : 'text', name: 'name', message: 'Component name (PascalCase, e.g. MyButton)' }, { onCancel });
73
+ const componentName = argName ?? name;
74
+ if (!componentName)
75
+ onCancel();
76
+ const kebab = toKebab(componentName);
77
+ const { target } = await prompts({
78
+ type: 'text',
79
+ name: 'target',
80
+ message: 'Target directory',
81
+ initial: path.join('src', 'components', kebab)
82
+ }, { onCancel });
83
+ const { spec } = await prompts({ type: 'confirm', name: 'spec', message: 'Include a vitest spec (adds vitest + @vue/test-utils to devDependencies)?', initial: false }, { onCancel });
84
+ const dir = path.resolve(process.cwd(), target);
85
+ if (existsSync(dir)) {
86
+ console.error(`${target} already exists`);
87
+ process.exit(1);
88
+ }
89
+ mkdirSync(dir, { recursive: true });
90
+ const files = readdirSync(componentTemplateDir).filter((f) => spec || f !== 'name.spec.ts');
91
+ for (const file of files) {
92
+ const content = substitute(readFileSync(path.join(componentTemplateDir, file), 'utf8'), componentName);
93
+ writeFileSync(path.join(dir, file.replace(/^name\./, `${kebab}.`)), content);
94
+ }
95
+ if (spec) {
96
+ const manifestPath = path.join(process.cwd(), 'package.json');
97
+ if (existsSync(manifestPath)) {
98
+ const manifest = JSON.parse(readFileSync(manifestPath, 'utf8'));
99
+ manifest.devDependencies = {
100
+ ...manifest.devDependencies,
101
+ '@vue/test-utils': '^2.4.6',
102
+ 'happy-dom': '^15.0.0',
103
+ vitest: '^5.0.5'
104
+ };
105
+ writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + '\n');
106
+ }
107
+ }
108
+ console.log(`\nCreated ${target}:`);
109
+ for (const file of files)
110
+ console.log(` ${file.replace(/^name\./, `${kebab}.`)}`);
111
+ };
112
+ const run = async () => {
113
+ if (process.argv[2] === 'component') {
114
+ await runComponent();
115
+ return;
116
+ }
117
+ const argDir = process.argv[2];
118
+ const { dir } = await prompts({
119
+ type: argDir ? null : 'text',
120
+ name: 'dir',
121
+ message: 'Project directory',
122
+ initial: 'ui0-app'
123
+ }, { onCancel });
124
+ const target = path.resolve(process.cwd(), argDir ?? dir);
125
+ const name = path.basename(target);
126
+ if (existsSync(target)) {
127
+ const { overwrite } = await prompts({ type: 'confirm', name: 'overwrite', message: `${name} exists - continue and fill missing files?`, initial: false }, { onCancel });
128
+ if (!overwrite)
129
+ process.exit(0);
130
+ }
131
+ const { features } = (await prompts({
132
+ type: 'multiselect',
133
+ name: 'features',
134
+ message: 'Packages to include',
135
+ choices: FEATURES.map((f) => ({ title: f.title, value: f.id, selected: f.id !== 'icon' })),
136
+ instructions: false
137
+ }, { onCancel }));
138
+ mkdirSync(target, { recursive: true });
139
+ cpSync(templateDir, target, { recursive: true });
140
+ rmSync(path.join(target, 'package.json.tpl'));
141
+ const picked = FEATURES.filter((f) => features.includes(f.id));
142
+ // package.json from the template manifest + chosen deps
143
+ const manifest = JSON.parse(readFileSync(path.join(templateDir, 'package.json.tpl'), 'utf8'));
144
+ manifest.name = name;
145
+ Object.assign(manifest.dependencies, ...picked.map((f) => f.deps));
146
+ writeFileSync(path.join(target, 'package.json'), JSON.stringify(manifest, null, 2) + '\n');
147
+ // App.vue: inject the demo sections of the picked features
148
+ const appVue = path.join(target, 'src/App.vue');
149
+ let app = readFileSync(appVue, 'utf8');
150
+ app = app
151
+ .replace('/* FEATURE_IMPORTS */', picked.map((f) => f.importLine).join('\n'))
152
+ .replace('/* FEATURE_SECTIONS */', picked.map((f) => f.section).join('\n\n'));
153
+ writeFileSync(appVue, app);
154
+ // the icon pack registers itself as a vue plugin
155
+ const mainTs = path.join(target, 'src/main.ts');
156
+ let main = readFileSync(mainTs, 'utf8');
157
+ if (features.includes('icon')) {
158
+ main = main
159
+ .replace("import { createUi0 } from '@ui0/core'", "import Icons from '@ui0/icons'\nimport { createUi0 } from '@ui0/core'")
160
+ .replace('app.use(ui0)', 'app.use(ui0)\napp.use(Icons)');
161
+ }
162
+ writeFileSync(mainTs, main);
163
+ console.log(`\nScaffolded ${name}:\n`);
164
+ console.log(` cd ${path.relative(process.cwd(), target)}`);
165
+ console.log(' npm install');
166
+ console.log(' npm run dev');
167
+ };
168
+ run();
package/package.json CHANGED
@@ -1,6 +1,26 @@
1
1
  {
2
2
  "name": "create-ui0",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.1",
4
+ "description": "Scaffold a ui0 (Vue + @ui0) app - npm create ui0",
5
+ "type": "module",
6
+ "bin": {
7
+ "create-ui0": "dist/index.js"
8
+ },
9
+ "files": [
10
+ "dist",
11
+ "template",
12
+ "template-component"
13
+ ],
14
+ "scripts": {
15
+ "create:build": "tsc -p tsconfig.json",
16
+ "create:publish": "npm publish --access public"
17
+ },
18
+ "dependencies": {
19
+ "prompts": "^2.4.2"
20
+ },
21
+ "devDependencies": {
22
+ "@types/node": "^20.14.8",
23
+ "@types/prompts": "^2.4.9",
24
+ "typescript": "~6.0.3"
25
+ }
26
+ }
@@ -0,0 +1,12 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>ui0 app</title>
7
+ </head>
8
+ <body>
9
+ <div id="app"></div>
10
+ <script type="module" src="/src/main.ts"></script>
11
+ </body>
12
+ </html>
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "ui0-app",
3
+ "version": "0.0.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "build": "vite build",
9
+ "preview": "vite preview"
10
+ },
11
+ "dependencies": {
12
+ "vue": "^3.5.45",
13
+ "@ui0/core": "^0.1.2",
14
+ "@ui0/theme": "^0.1.1"
15
+ },
16
+ "devDependencies": {
17
+ "@ui0/types": "^0.1.2",
18
+ "@vitejs/plugin-vue": "^6.0.1",
19
+ "sass-embedded": "^1.80.0",
20
+ "typescript": "~6.0.3",
21
+ "vite": "^8.3.5"
22
+ }
23
+ }
@@ -0,0 +1,44 @@
1
+ <template>
2
+ <ThemeProvider preset="app">
3
+ <App
4
+ title="ui0 app"
5
+ name="app"
6
+ >
7
+ <main :class="$style.page">
8
+ <h1>ui0 app</h1>
9
+
10
+ /* FEATURE_SECTIONS */
11
+ </main>
12
+ </App>
13
+ </ThemeProvider>
14
+ </template>
15
+
16
+ <script setup lang="ts">
17
+ import { App } from '@ui0/core'
18
+ import { ThemeProvider } from '@ui0/theme'
19
+ /* FEATURE_IMPORTS */
20
+ </script>
21
+
22
+ <style module lang="scss">
23
+ .page {
24
+ display: flex;
25
+ flex-direction: column;
26
+ gap: 24px;
27
+ padding: 32px;
28
+ font-family: system-ui, sans-serif;
29
+
30
+ h1 {
31
+ font-size: 20px;
32
+ }
33
+ h2 {
34
+ font-size: 14px;
35
+ opacity: 0.7;
36
+ margin-bottom: 8px;
37
+ }
38
+ }
39
+ .box {
40
+ padding: 12px 16px;
41
+ border: 1px solid rgba(127, 127, 127, 0.4);
42
+ border-radius: 6px;
43
+ }
44
+ </style>
@@ -0,0 +1,14 @@
1
+ import { createUi0 } from '@ui0/core'
2
+ import { createUi0Theme } from '@ui0/theme'
3
+ import { createApp } from 'vue'
4
+
5
+ import './assets/theme.css'
6
+ import config from '../ui0.config'
7
+ import App from './App.vue'
8
+
9
+ const ui0 = createUi0(config)
10
+ ui0.use(createUi0Theme(config.theme!))
11
+
12
+ const app = createApp(App)
13
+ app.use(ui0)
14
+ app.mount('#app')
@@ -0,0 +1,12 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ESNext",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "strict": true,
7
+ "jsx": "preserve",
8
+ "types": ["vue"],
9
+ "noEmit": true
10
+ },
11
+ "include": ["src/**/*.ts", "src/**/*.vue", "vite.config.ts", "ui0.config.ts"]
12
+ }
@@ -0,0 +1,23 @@
1
+ import type { Ui0CoreConfig } from '@ui0/types'
2
+
3
+ const config: Ui0CoreConfig = {
4
+ theme: {
5
+ default: 'app',
6
+ contracts: {
7
+ display: {}
8
+ },
9
+ presets: {
10
+ app: {
11
+ default: {
12
+ color: {
13
+ brand: ['rgb(86 59 177)', 4],
14
+ foreground: '#111',
15
+ background: '#fff'
16
+ }
17
+ }
18
+ }
19
+ }
20
+ }
21
+ }
22
+
23
+ export default config
@@ -0,0 +1,7 @@
1
+ import ui0Theme from '@ui0/theme/vite'
2
+ import Vue from '@vitejs/plugin-vue'
3
+ import { defineConfig } from 'vite'
4
+
5
+ export default defineConfig({
6
+ plugins: [ui0Theme({ out: 'src/assets/theme.css' }), Vue()]
7
+ })
@@ -0,0 +1,3 @@
1
+ .{%camel%} {
2
+ /* component base styles */
3
+ }
@@ -0,0 +1,12 @@
1
+ // @vitest-environment happy-dom
2
+ import { mount } from '@vue/test-utils'
3
+ import { describe, expect, it } from 'vitest'
4
+
5
+ import {%Pascal%} from './{%kebab%}.vue'
6
+
7
+ describe('{%kebab%}', () => {
8
+ it('renders', () => {
9
+ const wrapper = mount({%Pascal%})
10
+ expect(wrapper.find('[data-0="{%kebab%}"]').exists()).toBe(true)
11
+ })
12
+ })
@@ -0,0 +1,7 @@
1
+ export interface {%Pascal%}Props {
2
+ // add props here
3
+ }
4
+
5
+ export interface {%Pascal%}Slots {
6
+ default: () => void
7
+ }
@@ -0,0 +1,17 @@
1
+ <template>
2
+ <div
3
+ data-0="{%kebab%}"
4
+ :class="$styles.{%camel%}">
5
+ <slot />
6
+ </div>
7
+ </template>
8
+
9
+ <script setup lang="ts">
10
+ import { useCssModule } from 'vue'
11
+
12
+ import type { {%Pascal%}Props } from './{%kebab%}.types'
13
+ import './{%kebab%}.module.css'
14
+
15
+ const props = withDefaults(defineProps<{%Pascal%}Props>(), {})
16
+ const $styles = useCssModule()
17
+ </script>
package/README.md DELETED
@@ -1,3 +0,0 @@
1
- # Temporary Holding Version
2
-
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.