@component-hook/create-app 1.2.1 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -101,8 +101,9 @@ ln -s $(which npx) /usr/local/bin/npx
101
101
 
102
102
  These variables are injected at build time and are fully typed:
103
103
 
104
- | Variable Name | Description |
105
- | ---------------------- | -------------------------------------------- |
106
- | `VITE_APP_VERSION` | Read from `package.json` version |
107
- | `VITE_APP_BUILD_EPOCH` | Timestamp generated at build time |
108
- | `VITE_APP_MOCK` | Indicates whether the mock environment is on |
104
+ | Variable Name | Description |
105
+ | --------------------------- | -------------------------------------------- |
106
+ | `VITE_APP_VERSION` | Read from `package.json` version |
107
+ | `VITE_APP_LAST_COMMIT_HASH` | Last commit hash at build time |
108
+ | `VITE_APP_BUILD_EPOCH` | Populated as `Date.now()` at build time |
109
+ | `VITE_APP_MOCK` | Indicates whether the mock environment is on |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@component-hook/create-app",
3
- "version": "1.2.1",
3
+ "version": "1.3.0",
4
4
  "description": "Scaffolding a basic typescript project",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -1,24 +1,18 @@
1
1
  {
2
- "[nginx]": {
3
- "editor.defaultFormatter": "raynigon.nginx-formatter"
4
- },
5
2
  "editor.tabSize": 2,
6
- "editor.defaultFormatter": "esbenp.prettier-vscode",
7
- "debug.javascript.autoAttachFilter": "smart",
8
3
  "editor.formatOnSave": true,
9
- "[dart]": {
10
- "editor.formatOnSave": true,
11
- "editor.formatOnType": true,
12
- "editor.rulers": [120],
13
- "editor.selectionHighlight": false,
14
- "editor.suggest.snippetsPreventQuickSuggestions": false,
15
- "editor.suggestSelection": "first",
16
- "editor.tabCompletion": "onlySnippets",
17
- "editor.wordBasedSuggestions": "off"
4
+ "editor.defaultFormatter": "esbenp.prettier-vscode",
5
+
6
+ "[json]": {
7
+ "editor.defaultFormatter": "esbenp.prettier-vscode"
18
8
  },
9
+
10
+ "debug.javascript.autoAttachFilter": "smart",
11
+
12
+ "typescript.tsdk": "node_modules/typescript/lib",
13
+
19
14
  "workbench.editor.splitInGroupLayout": "vertical",
20
15
  "terminal.integrated.shellIntegration.enabled": true,
21
- "files.associations": {},
22
- "typescript.tsdk": "node_modules/typescript/lib",
16
+
23
17
  "cSpell.words": ["dotenv", "unplugin", "Sonarjs", "typecheck", "pxtorem", "norem", "partialize"]
24
18
  }
@@ -18,7 +18,8 @@ This project is built with [react](https://react.dev/) + [vite](https://vite.dev
18
18
  - if encounter npx: command not found, can execute `ln -s $(which npx) /usr/local/bin/npx` in zsh
19
19
  - Predefined and fully typed global variables:
20
20
  - `VITE_APP_VERSION` is read from `package.json` version at build time
21
- - `VITE_APP_BUILD_EPOCH` is populated as `new Date().getTime()` at build time
21
+ - `VITE_APP_LAST_COMMIT_HASH` is last commit hash at build time
22
+ - `VITE_APP_BUILD_EPOCH` is populated as `Date.now()` at build time
22
23
  - `VITE_APP_MOCK` is use mock environment
23
24
 
24
25
  ## Project Files
@@ -48,6 +48,7 @@
48
48
  "postcss-preset-env": "^10.4.0",
49
49
  "postcss-pxtorem": "^6.1.0",
50
50
  "prettier": "^3.6.2",
51
+ "prettier-plugin-tailwindcss": "^0.7.1",
51
52
  "tailwindcss": "^4.1.16",
52
53
  "typescript": "^5.9.3",
53
54
  "vite": "^7.2.1",
@@ -10,7 +10,7 @@ describe('home page HelloWorld component', () => {
10
10
 
11
11
  it('renders the correct content', () => {
12
12
  const title = 'Hello World!';
13
- const { version } = useConfigStore.getState();
13
+ const { appMeta } = useConfigStore.getState();
14
14
 
15
15
  renderComponent(<HelloWorld title={title} />);
16
16
  expect(screen.getByRole('heading', { name: title })).toBeInTheDocument();
@@ -19,7 +19,7 @@ describe('home page HelloWorld component', () => {
19
19
  expect(screen.getByText(/src\/app\.tsx/i)).toBeInTheDocument();
20
20
  expect(screen.getByText(/save to test hmr/i)).toBeInTheDocument();
21
21
  expect(screen.getByText(/click on the vite and react logos to learn more/i)).toBeInTheDocument();
22
- expect(screen.getByText(version)).toBeInTheDocument();
22
+ expect(screen.getByText(`${appMeta.version} - Built at: ${appMeta.builtAt.toLocaleString()}`)).toBeInTheDocument();
23
23
  expect(screen.getByRole('button', { name: t('language') })).toBeInTheDocument();
24
24
  });
25
25
 
@@ -9,7 +9,7 @@ interface Props {
9
9
  export function HelloWorld({ title = '' }: Props) {
10
10
  const [count, setCount] = useState(0);
11
11
  const { t, i18n } = useTranslation();
12
- const { version } = useConfigStore();
12
+ const { appMeta } = useConfigStore();
13
13
 
14
14
  function changeLanguage() {
15
15
  const language = i18n.language === 'zh-TW' ? 'en-US' : 'zh-TW';
@@ -34,7 +34,9 @@ export function HelloWorld({ title = '' }: Props) {
34
34
  </div>
35
35
 
36
36
  <p className="text-[#888]">Click on the Vite and React logos to learn more</p>
37
- <p className="text-[#888]">{version}</p>
37
+ <p className="mt-1 text-[#888]">
38
+ {appMeta.version} - Built at: {appMeta.builtAt.toLocaleString()}
39
+ </p>
38
40
 
39
41
  <button
40
42
  className="my-4"
@@ -26,7 +26,7 @@ export function Home() {
26
26
  >
27
27
  <SvgIcon
28
28
  name="react"
29
- className={`${styles.logo} hover:filter-[drop-shadow(0_0_2em_#61dafbaa)] motion-safe:animate-logo-spin`}
29
+ className={`${styles.logo} motion-safe:animate-logo-spin hover:filter-[drop-shadow(0_0_2em_#61dafbaa)]`}
30
30
  />
31
31
  </a>
32
32
  </div>
@@ -1,7 +1,7 @@
1
1
  import i18n from 'i18next';
2
2
  import { initReactI18next } from 'react-i18next';
3
- import enUS from './locales/en-US.json';
4
- import zhTW from './locales/zh-TW.json';
3
+ import enUS from './locales/en-US.json' with { type: 'json' };
4
+ import zhTW from './locales/zh-TW.json' with { type: 'json' };
5
5
 
6
6
  i18n.use(initReactI18next).init({
7
7
  lng: navigator.language,
@@ -1,17 +1,19 @@
1
1
  import { createPersistedStore } from '../middleware';
2
2
 
3
3
  interface ConfigState {
4
- isDev: boolean;
5
- version: string;
4
+ appMeta: {
5
+ version: string;
6
+ builtAt: Date;
7
+ };
6
8
  }
7
9
 
8
- const versionString = import.meta.env.DEV
9
- ? `${import.meta.env.VITE_APP_VERSION}-dev`
10
- : import.meta.env.VITE_APP_VERSION;
10
+ const versionString = `${import.meta.env.VITE_APP_VERSION}-${import.meta.env.VITE_APP_LAST_COMMIT_HASH}`;
11
11
 
12
12
  const defaultState = {
13
- isDev: import.meta.env.DEV,
14
- version: versionString,
13
+ appMeta: {
14
+ version: import.meta.env.MODE === 'development' ? `${versionString}-dev` : versionString,
15
+ builtAt: new Date(Number(import.meta.env.VITE_APP_BUILD_EPOCH)),
16
+ },
15
17
  };
16
18
 
17
19
  export const useConfigStore = createPersistedStore<ConfigState>(
@@ -4,17 +4,7 @@ import tailwindcss from '@tailwindcss/vite';
4
4
  import react from '@vitejs/plugin-react-swc';
5
5
  import { defineConfig } from 'vite';
6
6
  import { createSvgIconsPlugin } from 'vite-plugin-svg-icons';
7
- import packageJson from './package.json' with { type: 'json' };
8
-
9
- process.env.VITE_APP_VERSION = packageJson.version;
10
-
11
- if (process.env.MOCK) {
12
- process.env.VITE_APP_MOCK = 'service-worker';
13
- }
14
-
15
- if (process.env.NODE_ENV === 'production') {
16
- process.env.VITE_APP_BUILD_EPOCH = Date.now().toString();
17
- }
7
+ import './scripts/build-info';
18
8
 
19
9
  export default defineConfig({
20
10
  base: './',
@@ -1,10 +1,10 @@
1
- # Click this link to view browser coverage https://browsersl.ist/#q=%3E+1.5%25%0Aios+%3E%3D+12%0Achrome+%3E%3D+49%0Anot+ie+%3C%3D+11%0Alast+2+versions%0Anot+dead
1
+ # Click this link to view browser coverage https://browsersl.ist/#q=%3E+1.5%25%0Aios+%3E%3D+13%0Achrome+%3E%3D+60%0Anot+ie+%3C%3D+11%0Alast+2+versions%0Anot+dead
2
2
 
3
3
  # Browsers that support
4
4
 
5
5
  > 1.5%
6
- ios >= 12
7
- chrome >= 49
6
+ ios >= 13
7
+ chrome >= 60
8
8
  not ie <= 11
9
9
  last 2 versions
10
10
  not dead
@@ -5,41 +5,17 @@ description: Install dependencies
5
5
  runs:
6
6
  using: composite
7
7
  steps:
8
- - name: install Node.js
9
- uses: actions/setup-node@v6
10
- with:
11
- node-version: 20.19.5
12
-
13
8
  - name: install pnpm
14
9
  uses: pnpm/action-setup@v4
15
10
  with:
16
11
  run_install: false
17
12
 
18
- - name: get pnpm store directory
19
- id: pnpm-cache
20
- shell: bash
21
- run: |
22
- echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
23
-
24
- - name: setup pnpm cache
25
- uses: actions/cache@v4
26
- with:
27
- path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
28
- key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
29
- restore-keys: |
30
- ${{ runner.os }}-pnpm-store-
31
-
32
- - name: setup node_modules cache
33
- id: dependencies-cache
34
- uses: actions/cache@v4
13
+ - name: install Node.js
14
+ uses: actions/setup-node@v6
35
15
  with:
36
- path: |
37
- **/node_modules
38
- key: ${{ runner.os }}-node-modules-${{ hashFiles('**/pnpm-lock.yaml') }}
39
- restore-keys: |
40
- ${{ runner.os }}-node-modules-
16
+ node-version: 24.11.1
17
+ cache: 'pnpm'
41
18
 
42
19
  - name: install dependencies
43
- if: steps.dependencies-cache.outputs.cache-hit != 'true'
44
20
  shell: bash
45
21
  run: pnpm install
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "https://docs.renovatebot.com/renovate-schema.json",
3
- "extends": ["config:base", "group:allNonMajor", "schedule:weekly", ":semanticCommitTypeAll(chore)"],
3
+ "extends": ["config:recommended", "schedule:weekly", "group:allNonMajor"],
4
4
  "labels": ["dependencies"],
5
5
  "rangeStrategy": "bump",
6
6
  "packageRules": [
@@ -8,7 +8,7 @@ jobs:
8
8
  CI:
9
9
  runs-on: ubuntu-latest
10
10
  steps:
11
- - uses: actions/checkout@v5
11
+ - uses: actions/checkout@v6
12
12
  - name: setup environment
13
13
  uses: './.github/actions/setup-environment'
14
14
 
@@ -16,7 +16,7 @@ jobs:
16
16
  runs-on: ubuntu-latest
17
17
  needs: CI
18
18
  steps:
19
- - uses: actions/checkout@v5
19
+ - uses: actions/checkout@v6
20
20
  # - name: setup environment
21
21
  # uses: './.github/actions/setup-environment'
22
22
 
@@ -12,6 +12,6 @@ jobs:
12
12
  runs-on: ubuntu-latest
13
13
  needs: CI
14
14
  steps:
15
- - uses: actions/checkout@v5
15
+ - uses: actions/checkout@v6
16
16
  # - name: setup environment
17
17
  # uses: './.github/actions/setup-environment'
@@ -1,5 +1,5 @@
1
1
  variables:
2
- NODE_VERSION: '20'
2
+ NODE_VERSION: '22'
3
3
  CACHE_KEY: '${CI_PROJECT_PATH_SLUG}-${CI_COMMIT_REF_SLUG}'
4
4
 
5
5
  image: node:${NODE_VERSION}-alpine
@@ -14,9 +14,6 @@ dist-ssr
14
14
  .env
15
15
 
16
16
  # Editor directories and files
17
- .vscode/*
18
- !.vscode/extensions.json
19
- !.vscode/settings.json
20
17
  .idea
21
18
  .DS_Store
22
19
  *.suo
@@ -1,6 +1,7 @@
1
1
  /** @type {import('prettier').RequiredOptions} */
2
2
 
3
3
  export default {
4
+ $schema: 'https://json.schemastore.org/prettierrc',
4
5
  singleQuote: true,
5
6
  semi: true,
6
7
  endOfLine: 'lf',
@@ -8,4 +9,5 @@ export default {
8
9
  proseWrap: 'preserve',
9
10
  arrowParens: 'avoid',
10
11
  singleAttributePerLine: true,
12
+ plugins: ['prettier-plugin-tailwindcss'],
11
13
  };
@@ -0,0 +1,15 @@
1
+ import { execFileSync } from 'node:child_process';
2
+ import os from 'node:os';
3
+ import packageJson from '../../package.json' with { type: 'json' };
4
+
5
+ const platform = os.platform();
6
+ const git = platform === 'win32' ? String.raw`C:\Program Files\Git\bin\git.exe` : '/usr/bin/git';
7
+ const hash = execFileSync(git, ['rev-parse', 'HEAD']).toString().trim().slice(0, 7);
8
+
9
+ process.env.VITE_APP_VERSION = packageJson.version;
10
+ process.env.VITE_APP_LAST_COMMIT_HASH = `${hash}`;
11
+ process.env.VITE_APP_BUILD_EPOCH = Date.now().toString();
12
+
13
+ if (process.env.MOCK) {
14
+ process.env.VITE_APP_MOCK = 'service-worker';
15
+ }
@@ -8,5 +8,5 @@
8
8
  "moduleResolution": "bundler",
9
9
  "types": ["node"]
10
10
  },
11
- "include": ["vite.config.ts", "package.json", "vitest.config.ts", "playwright.config.ts"]
11
+ "include": ["vite.config.ts", "package.json", "vitest.config.ts", "playwright.config.ts", "scripts/**/*.ts"]
12
12
  }
@@ -1,27 +1,18 @@
1
1
  {
2
- "[nginx]": {
3
- "editor.defaultFormatter": "raynigon.nginx-formatter"
4
- },
5
2
  "editor.tabSize": 2,
6
- "editor.defaultFormatter": "esbenp.prettier-vscode",
7
- "debug.javascript.autoAttachFilter": "smart",
8
3
  "editor.formatOnSave": true,
9
- "[dart]": {
10
- "editor.formatOnSave": true,
11
- "editor.formatOnType": true,
12
- "editor.rulers": [120],
13
- "editor.selectionHighlight": false,
14
- "editor.suggest.snippetsPreventQuickSuggestions": false,
15
- "editor.suggestSelection": "first",
16
- "editor.tabCompletion": "onlySnippets",
17
- "editor.wordBasedSuggestions": "off"
4
+ "editor.defaultFormatter": "esbenp.prettier-vscode",
5
+
6
+ "[json]": {
7
+ "editor.defaultFormatter": "esbenp.prettier-vscode"
18
8
  },
9
+
10
+ "debug.javascript.autoAttachFilter": "smart",
11
+
12
+ "typescript.tsdk": "node_modules/typescript/lib",
13
+
19
14
  "workbench.editor.splitInGroupLayout": "vertical",
20
15
  "terminal.integrated.shellIntegration.enabled": true,
21
- "volar.format.initialIndent": {
22
- "html": true
23
- },
24
- "files.associations": {},
25
- "typescript.tsdk": "node_modules/typescript/lib",
26
- "cSpell.words": ["dotenv", "pinia", "persistedstate", "unplugin", "Sonarjs", "typecheck"]
16
+
17
+ "cSpell.words": ["dotenv", "pinia", "persistedstate", "unplugin", "Sonarjs", "typecheck", "pxtorem", "norem"]
27
18
  }
@@ -19,7 +19,8 @@ This project is built with [vue3](https://vuejs.org/) + [vite](https://vite.dev/
19
19
  - if encounter npx: command not found, can execute `ln -s $(which npx) /usr/local/bin/npx` in zsh
20
20
  - Predefined and fully typed global variables:
21
21
  - `VITE_APP_VERSION` is read from `package.json` version at build time
22
- - `VITE_APP_BUILD_EPOCH` is populated as `new Date().getTime()` at build time
22
+ - `VITE_APP_LAST_COMMIT_HASH` is last commit hash at build time
23
+ - `VITE_APP_BUILD_EPOCH` is populated as `Date.now()` at build time
23
24
  - `VITE_APP_MOCK` is use mock environment
24
25
 
25
26
  ## Project Files
@@ -47,6 +47,7 @@
47
47
  "postcss-preset-env": "^10.4.0",
48
48
  "postcss-pxtorem": "^6.1.0",
49
49
  "prettier": "^3.6.2",
50
+ "prettier-plugin-tailwindcss": "^0.7.1",
50
51
  "rollup-plugin-visualizer": "^6.0.5",
51
52
  "tailwindcss": "^4.1.16",
52
53
  "typescript": "^5.9.3",
@@ -1,10 +1,6 @@
1
1
  /// <reference types="vite/client" />
2
2
 
3
- declare module '*.vue' {
4
- import { DefineComponent } from 'vue';
5
- const component: DefineComponent<any, {}, any>;
6
- export default component;
7
- }
3
+ declare module 'virtual:svg-icons-register';
8
4
 
9
5
  interface ImportMetaEnv extends Readonly<Record<string, string>> {
10
6
  readonly VITE_APP_VERSION: string;
@@ -6,17 +6,7 @@ import vue from '@vitejs/plugin-vue';
6
6
  import { visualizer } from 'rollup-plugin-visualizer';
7
7
  import { defineConfig } from 'vite';
8
8
  import { createSvgIconsPlugin } from 'vite-plugin-svg-icons';
9
- import packageJson from './package.json' with { type: 'json' };
10
-
11
- process.env.VITE_APP_VERSION = packageJson.version;
12
-
13
- if (process.env.MOCK) {
14
- process.env.VITE_APP_MOCK = 'service-worker';
15
- }
16
-
17
- if (process.env.NODE_ENV === 'production') {
18
- process.env.VITE_APP_BUILD_EPOCH = Date.now().toString();
19
- }
9
+ import './scripts/build-info';
20
10
 
21
11
  export default defineConfig({
22
12
  base: './',