Compare commits
76 Commits
Author | SHA1 | Date | |
---|---|---|---|
74a8229e43 | |||
859cbc733d | |||
d32d47b706 | |||
fd90cfe895 | |||
c48f48fc8b | |||
e21e7f0850 | |||
5f561527f9 | |||
9f5f568c3f | |||
39a31a4304 | |||
b629a7d70b | |||
4003944139 | |||
83d374dffd | |||
49a5a66440 | |||
20a53d4d92 | |||
fe02b990b3 | |||
c013fbf42e | |||
949f273317 | |||
7b2ae01112 | |||
53421e79d8 | |||
eec803e512 | |||
6d11515b47 | |||
6a7b4c8b7e | |||
25c0162c39 | |||
e66d1f05e4 | |||
b1a8a5527e | |||
0017781516 | |||
da0cd0ed71 | |||
4890afb2f0 | |||
77bb8bf22e | |||
ff2ebc076d | |||
d71b082b75 | |||
f6680dc45e | |||
cb4a02d1ef | |||
9e39444daa | |||
6ec5d275bb | |||
f5d78f3b40 | |||
68546939ca | |||
08e9849116 | |||
f25ade3e97 | |||
6870c40e57 | |||
5e99a37a55 | |||
44d1cefc9f | |||
83bafbb6f6 | |||
b90234e226 | |||
a039a58f8e | |||
d861fcb2d4 | |||
3a1138da83 | |||
bdbd2b6e08 | |||
43f5643ab1 | |||
99e4dcb2e8 | |||
52f6afb69a | |||
75c1e3d8f2 | |||
2a1ed874b2 | |||
58fb0bc3d5 | |||
b1fbd38dba | |||
fad5e046d3 | |||
eb1498c242 | |||
6b3d4d1ecf | |||
16ea9bb8bd | |||
7e7aa098f9 | |||
df4350d29d | |||
81eb713dee | |||
90637b1466 | |||
6c3b78d0fe | |||
f0c7cf34e6 | |||
b9b5736e5e | |||
b73acf904a | |||
6614529122 | |||
9ff5e9a70f | |||
9dbf511d50 | |||
b7a239abca | |||
181fad27fd | |||
59c3deb83a | |||
c76403a34c | |||
23b361f26a | |||
9f7d447bc7 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -3,7 +3,6 @@
|
|||||||
# artifacts
|
# artifacts
|
||||||
coverage/
|
coverage/
|
||||||
public/
|
public/
|
||||||
pages/
|
|
||||||
|
|
||||||
# installs
|
# installs
|
||||||
node_modules/
|
node_modules/
|
||||||
@@ -17,4 +16,4 @@ node_modules/
|
|||||||
dist/
|
dist/
|
||||||
dist_*/
|
dist_*/
|
||||||
|
|
||||||
# custom
|
#------# custom
|
1
assets/overrides.json
Normal file
1
assets/overrides.json
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{}
|
@@ -6,8 +6,8 @@ on:
|
|||||||
- '**'
|
- '**'
|
||||||
|
|
||||||
env:
|
env:
|
||||||
IMAGE: code.foss.global/hosttoday/ht-docker-node:npmci
|
IMAGE: code.foss.global/host.today/ht-docker-node:npmci
|
||||||
NPMCI_COMPUTED_REPOURL: https://${-{gitea.repository_owner}-}:${-{secrets.GITEA_TOKEN}-}@{{git.host}}/${-{gitea.repository}-}.git
|
NPMCI_COMPUTED_REPOURL: https://${-{gitea.repository_owner}-}:${-{secrets.GITEA_TOKEN}-}@{{module.githost}}/${-{gitea.repository}-}.git
|
||||||
NPMCI_TOKEN_NPM: ${-{secrets.NPMCI_TOKEN_NPM}-}
|
NPMCI_TOKEN_NPM: ${-{secrets.NPMCI_TOKEN_NPM}-}
|
||||||
NPMCI_TOKEN_NPM2: ${-{secrets.NPMCI_TOKEN_NPM2}-}
|
NPMCI_TOKEN_NPM2: ${-{secrets.NPMCI_TOKEN_NPM2}-}
|
||||||
NPMCI_GIT_GITHUBTOKEN: ${-{secrets.NPMCI_GIT_GITHUBTOKEN}-}
|
NPMCI_GIT_GITHUBTOKEN: ${-{secrets.NPMCI_GIT_GITHUBTOKEN}-}
|
||||||
|
@@ -6,8 +6,8 @@ on:
|
|||||||
- '*'
|
- '*'
|
||||||
|
|
||||||
env:
|
env:
|
||||||
IMAGE: code.foss.global/hosttoday/ht-docker-node:npmci
|
IMAGE: code.foss.global/host.today/ht-docker-node:npmci
|
||||||
NPMCI_COMPUTED_REPOURL: https://${-{gitea.repository_owner}-}:${-{secrets.GITEA_TOKEN}-}@{{git.host}}/${-{gitea.repository}-}.git
|
NPMCI_COMPUTED_REPOURL: https://${-{gitea.repository_owner}-}:${-{secrets.GITEA_TOKEN}-}@{{module.githost}}/${-{gitea.repository}-}.git
|
||||||
NPMCI_TOKEN_NPM: ${-{secrets.NPMCI_TOKEN_NPM}-}
|
NPMCI_TOKEN_NPM: ${-{secrets.NPMCI_TOKEN_NPM}-}
|
||||||
NPMCI_TOKEN_NPM2: ${-{secrets.NPMCI_TOKEN_NPM2}-}
|
NPMCI_TOKEN_NPM2: ${-{secrets.NPMCI_TOKEN_NPM2}-}
|
||||||
NPMCI_GIT_GITHUBTOKEN: ${-{secrets.NPMCI_GIT_GITHUBTOKEN}-}
|
NPMCI_GIT_GITHUBTOKEN: ${-{secrets.NPMCI_GIT_GITHUBTOKEN}-}
|
||||||
|
@@ -27,8 +27,8 @@ auditProductionDependencies:
|
|||||||
image: code.foss.global/hosttoday/ht-docker-node:npmci
|
image: code.foss.global/hosttoday/ht-docker-node:npmci
|
||||||
stage: security
|
stage: security
|
||||||
script:
|
script:
|
||||||
- npmci command npm config set registry https://registry.npmjs.org
|
- npmci command npm config set registry https://registry.npmjs.org
|
||||||
- npmci command pnpm audit --audit-level=high --prod
|
- npmci command pnpm audit --audit-level=high --prod
|
||||||
tags:
|
tags:
|
||||||
- private
|
- private
|
||||||
- docker
|
- docker
|
||||||
|
@@ -7,7 +7,7 @@ on:
|
|||||||
|
|
||||||
env:
|
env:
|
||||||
IMAGE: code.foss.global/hosttoday/ht-docker-node:npmci
|
IMAGE: code.foss.global/hosttoday/ht-docker-node:npmci
|
||||||
NPMCI_COMPUTED_REPOURL: https://${-{gitea.repository_owner}-}:${-{secrets.GITEA_TOKEN}-}@{{git.host}}/${-{gitea.repository}-}.git
|
NPMCI_COMPUTED_REPOURL: https://${-{gitea.repository_owner}-}:${-{secrets.GITEA_TOKEN}-}@{{module.githost}}/${-{gitea.repository}-}.git
|
||||||
NPMCI_TOKEN_NPM: ${-{secrets.NPMCI_TOKEN_NPM}-}
|
NPMCI_TOKEN_NPM: ${-{secrets.NPMCI_TOKEN_NPM}-}
|
||||||
NPMCI_TOKEN_NPM2: ${-{secrets.NPMCI_TOKEN_NPM2}-}
|
NPMCI_TOKEN_NPM2: ${-{secrets.NPMCI_TOKEN_NPM2}-}
|
||||||
NPMCI_GIT_GITHUBTOKEN: ${-{secrets.NPMCI_GIT_GITHUBTOKEN}-}
|
NPMCI_GIT_GITHUBTOKEN: ${-{secrets.NPMCI_GIT_GITHUBTOKEN}-}
|
||||||
|
@@ -7,7 +7,7 @@ on:
|
|||||||
|
|
||||||
env:
|
env:
|
||||||
IMAGE: code.foss.global/hosttoday/ht-docker-node:npmci
|
IMAGE: code.foss.global/hosttoday/ht-docker-node:npmci
|
||||||
NPMCI_COMPUTED_REPOURL: https://${-{gitea.repository_owner}-}:${-{secrets.GITEA_TOKEN}-}@{{git.host}}/${-{gitea.repository}-}.git
|
NPMCI_COMPUTED_REPOURL: https://${-{gitea.repository_owner}-}:${-{secrets.GITEA_TOKEN}-}@{{module.githost}}/${-{gitea.repository}-}.git
|
||||||
NPMCI_TOKEN_NPM: ${-{secrets.NPMCI_TOKEN_NPM}-}
|
NPMCI_TOKEN_NPM: ${-{secrets.NPMCI_TOKEN_NPM}-}
|
||||||
NPMCI_TOKEN_NPM2: ${-{secrets.NPMCI_TOKEN_NPM2}-}
|
NPMCI_TOKEN_NPM2: ${-{secrets.NPMCI_TOKEN_NPM2}-}
|
||||||
NPMCI_GIT_GITHUBTOKEN: ${-{secrets.NPMCI_GIT_GITHUBTOKEN}-}
|
NPMCI_GIT_GITHUBTOKEN: ${-{secrets.NPMCI_GIT_GITHUBTOKEN}-}
|
||||||
|
@@ -27,8 +27,8 @@ auditProductionDependencies:
|
|||||||
image: code.foss.global/hosttoday/ht-docker-node:npmci
|
image: code.foss.global/hosttoday/ht-docker-node:npmci
|
||||||
stage: security
|
stage: security
|
||||||
script:
|
script:
|
||||||
- npmci command npm config set registry https://registry.npmjs.org
|
- npmci command npm config set registry https://registry.npmjs.org
|
||||||
- npmci command pnpm audit --audit-level=high --prod
|
- npmci command pnpm audit --audit-level=high --prod
|
||||||
tags:
|
tags:
|
||||||
- private
|
- private
|
||||||
- docker
|
- docker
|
||||||
|
@@ -7,7 +7,7 @@ on:
|
|||||||
|
|
||||||
env:
|
env:
|
||||||
IMAGE: code.foss.global/hosttoday/ht-docker-node:npmci
|
IMAGE: code.foss.global/hosttoday/ht-docker-node:npmci
|
||||||
NPMCI_COMPUTED_REPOURL: https://${-{gitea.repository_owner}-}:${-{secrets.GITEA_TOKEN}-}@{{gi.host}}/${-{gitea.repository}-}.git
|
NPMCI_COMPUTED_REPOURL: https://${-{gitea.repository_owner}-}:${-{secrets.GITEA_TOKEN}-}@{{module.githost}}/${-{gitea.repository}-}.git
|
||||||
NPMCI_TOKEN_NPM: ${-{secrets.NPMCI_TOKEN_NPM}-}
|
NPMCI_TOKEN_NPM: ${-{secrets.NPMCI_TOKEN_NPM}-}
|
||||||
NPMCI_TOKEN_NPM2: ${-{secrets.NPMCI_TOKEN_NPM2}-}
|
NPMCI_TOKEN_NPM2: ${-{secrets.NPMCI_TOKEN_NPM2}-}
|
||||||
NPMCI_GIT_GITHUBTOKEN: ${-{secrets.NPMCI_GIT_GITHUBTOKEN}-}
|
NPMCI_GIT_GITHUBTOKEN: ${-{secrets.NPMCI_GIT_GITHUBTOKEN}-}
|
||||||
|
@@ -7,7 +7,7 @@ on:
|
|||||||
|
|
||||||
env:
|
env:
|
||||||
IMAGE: code.foss.global/hosttoday/ht-docker-node:npmci
|
IMAGE: code.foss.global/hosttoday/ht-docker-node:npmci
|
||||||
NPMCI_COMPUTED_REPOURL: https://${-{gitea.repository_owner}-}:${-{secrets.GITEA_TOKEN}-}@{{git.host}}/${-{gitea.repository}-}.git
|
NPMCI_COMPUTED_REPOURL: https://${-{gitea.repository_owner}-}:${-{secrets.GITEA_TOKEN}-}@{{module.githost}}/${-{gitea.repository}-}.git
|
||||||
NPMCI_TOKEN_NPM: ${-{secrets.NPMCI_TOKEN_NPM}-}
|
NPMCI_TOKEN_NPM: ${-{secrets.NPMCI_TOKEN_NPM}-}
|
||||||
NPMCI_TOKEN_NPM2: ${-{secrets.NPMCI_TOKEN_NPM2}-}
|
NPMCI_TOKEN_NPM2: ${-{secrets.NPMCI_TOKEN_NPM2}-}
|
||||||
NPMCI_GIT_GITHUBTOKEN: ${-{secrets.NPMCI_GIT_GITHUBTOKEN}-}
|
NPMCI_GIT_GITHUBTOKEN: ${-{secrets.NPMCI_GIT_GITHUBTOKEN}-}
|
||||||
@@ -89,8 +89,8 @@ jobs:
|
|||||||
npmci docker login
|
npmci docker login
|
||||||
npmci docker build
|
npmci docker build
|
||||||
npmci docker test
|
npmci docker test
|
||||||
# npmci docker push {{git.host}}
|
# npmci docker push {{module.githost}}
|
||||||
npmci docker push {{git.host}}
|
npmci docker push {{module.githost}}
|
||||||
|
|
||||||
metadata:
|
metadata:
|
||||||
needs: test
|
needs: test
|
||||||
|
@@ -25,8 +25,8 @@ auditProductionDependencies:
|
|||||||
image: code.foss.global/hosttoday/ht-docker-node:npmci
|
image: code.foss.global/hosttoday/ht-docker-node:npmci
|
||||||
stage: security
|
stage: security
|
||||||
script:
|
script:
|
||||||
- npmci command npm config set registry https://registry.npmjs.org
|
- npmci command npm config set registry https://registry.npmjs.org
|
||||||
- npmci command pnpm audit --audit-level=high --prod
|
- npmci command pnpm audit --audit-level=high --prod
|
||||||
tags:
|
tags:
|
||||||
- private
|
- private
|
||||||
- docker
|
- docker
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
# gitzone dockerfile_service
|
# gitzone dockerfile_service
|
||||||
## STAGE 1 // BUILD
|
## STAGE 1 // BUILD
|
||||||
FROM code.foss.global/hosttoday/ht-docker-node:npmci as node1
|
FROM code.foss.global/host.today/ht-docker-node:npmci as node1
|
||||||
COPY ./ /app
|
COPY ./ /app
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
ARG NPMCI_TOKEN_NPM2
|
ARG NPMCI_TOKEN_NPM2
|
||||||
@@ -12,7 +12,7 @@ RUN pnpm run build
|
|||||||
|
|
||||||
# gitzone dockerfile_service
|
# gitzone dockerfile_service
|
||||||
## STAGE 2 // install production
|
## STAGE 2 // install production
|
||||||
FROM code.foss.global/hosttoday/ht-docker-node:npmci as node2
|
FROM code.foss.global/host.today/ht-docker-node:npmci as node2
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY --from=node1 /app /app
|
COPY --from=node1 /app /app
|
||||||
RUN rm -rf .pnpm-store
|
RUN rm -rf .pnpm-store
|
||||||
@@ -24,7 +24,7 @@ RUN rm -rf node_modules/ && pnpm install --prod
|
|||||||
|
|
||||||
|
|
||||||
## STAGE 3 // rebuild dependencies for alpine
|
## STAGE 3 // rebuild dependencies for alpine
|
||||||
FROM code.foss.global/hosttoday/ht-docker-node:alpinenpmci as node3
|
FROM code.foss.global/host.today/ht-docker-node:alpine_npmci as node3
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY --from=node2 /app /app
|
COPY --from=node2 /app /app
|
||||||
ARG NPMCI_TOKEN_NPM2
|
ARG NPMCI_TOKEN_NPM2
|
||||||
@@ -34,7 +34,7 @@ RUN pnpm config set store-dir .pnpm-store
|
|||||||
RUN pnpm rebuild -r
|
RUN pnpm rebuild -r
|
||||||
|
|
||||||
## STAGE 4 // the final production image with all dependencies in place
|
## STAGE 4 // the final production image with all dependencies in place
|
||||||
FROM code.foss.global/hosttoday/ht-docker-node:alpine as node4
|
FROM code.foss.global/host.today/ht-docker-node:alpine as node4
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY --from=node3 /app /app
|
COPY --from=node3 /app /app
|
||||||
|
|
||||||
|
@@ -6,7 +6,6 @@ fileName: .gitignore
|
|||||||
# artifacts
|
# artifacts
|
||||||
coverage/
|
coverage/
|
||||||
public/
|
public/
|
||||||
pages/
|
|
||||||
|
|
||||||
# installs
|
# installs
|
||||||
node_modules/
|
node_modules/
|
||||||
@@ -20,4 +19,4 @@ node_modules/
|
|||||||
dist/
|
dist/
|
||||||
dist_*/
|
dist_*/
|
||||||
|
|
||||||
# custom
|
#------# custom
|
@@ -2,4 +2,3 @@ runafter:
|
|||||||
- git add -A && git commit -m initial
|
- git add -A && git commit -m initial
|
||||||
- git push origin master
|
- git push origin master
|
||||||
- gitzone meta update
|
- gitzone meta update
|
||||||
|
|
8
assets/templates/npm/ts/paths.ts
Normal file
8
assets/templates/npm/ts/paths.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
---
|
||||||
|
fileName: paths.ts
|
||||||
|
---
|
||||||
|
import * as plugins from './plugins.js';
|
||||||
|
export const packageDir = plugins.path.join(
|
||||||
|
plugins.smartpath.get.dirnameFromImportMetaUrl(import.meta.url),
|
||||||
|
'../'
|
||||||
|
);
|
12
assets/templates/npm/ts/plugins.ts
Normal file
12
assets/templates/npm/ts/plugins.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
---
|
||||||
|
fileName: plugins.ts
|
||||||
|
---
|
||||||
|
// native scope
|
||||||
|
import * as path from 'path';
|
||||||
|
|
||||||
|
export { path };
|
||||||
|
|
||||||
|
// @push.rocks scope
|
||||||
|
import * as smartpath from '@push.rocks/smartpath';
|
||||||
|
|
||||||
|
export { smartpath };
|
@@ -1,7 +0,0 @@
|
|||||||
---
|
|
||||||
fileName: {{module.name}}.plugins.ts
|
|
||||||
---
|
|
||||||
const removeme = {};
|
|
||||||
export {
|
|
||||||
removeme
|
|
||||||
}
|
|
@@ -21,14 +21,14 @@ fileName: package.json
|
|||||||
"@git.zone/tsrun": "^1.2.8",
|
"@git.zone/tsrun": "^1.2.8",
|
||||||
"@git.zone/tstest": "^1.0.28",
|
"@git.zone/tstest": "^1.0.28",
|
||||||
"@git.zone/tswatch": "^2.0.1",
|
"@git.zone/tswatch": "^2.0.1",
|
||||||
"@push.rocks/tapbundle": "^5.0.3"
|
"@push.rocks/tapbundle": "^5.5.4"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@api.global/typedserver": "^1.0.24",
|
"@api.global/typedserver": "^3.0.53",
|
||||||
"@push.rocks/projectinfo": "^5.0.1",
|
"@push.rocks/projectinfo": "^5.0.1",
|
||||||
"@push.rocks/qenv": "^4.0.10",
|
"@push.rocks/qenv": "^6.1.0",
|
||||||
"@push.rocks/smartdata": "^5.0.7",
|
"@push.rocks/smartdata": "^5.0.7",
|
||||||
"@push.rocks/smartpath": "^5.0.5",
|
"@push.rocks/smartpath": "^5.0.5",
|
||||||
"@push.rocks/smartstate": "^2.0.0"
|
"@push.rocks/smartstate": "^2.0.0"
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
---
|
---
|
||||||
fileName: {{module.name}}.classes.{{module.name}}db.ts
|
fileName: classes.{{module.name}}db.ts
|
||||||
---
|
---
|
||||||
import * as plugins from './{{module.name}}.plugins.js';
|
import * as plugins from './{{module.name}}.plugins.js';
|
||||||
import { {{module.name}} } from './{{module.name}}.classes.{{module.name}}.js';
|
import { {{module.name}} } from './classes.{{module.name}}.js';
|
||||||
|
|
||||||
|
|
||||||
|
|
@@ -1,9 +1,9 @@
|
|||||||
---
|
---
|
||||||
fileName: {{module.name}}.classes.{{module.name}}.ts
|
fileName: classes.{{module.name}}.ts
|
||||||
---
|
---
|
||||||
import * as plugins from './{{module.name}}.plugins.js';
|
import * as plugins from './{{module.name}}.plugins.js';
|
||||||
import * as paths from './{{module.name}}.paths.js';
|
import * as paths from './{{module.name}}.paths.js';
|
||||||
import { {{module.name}}Db } from './{{module.name}}.db.js'
|
import { {{module.name}}Db } from './classes.{{module.name}}db.js'
|
||||||
|
|
||||||
export class {{module.name}} {
|
export class {{module.name}} {
|
||||||
public projectinfo: plugins.projectinfo.ProjectInfo;
|
public projectinfo: plugins.projectinfo.ProjectInfo;
|
@@ -15,8 +15,7 @@ export {
|
|||||||
loleServiceserver
|
loleServiceserver
|
||||||
}
|
}
|
||||||
|
|
||||||
// pushrocks scope
|
// @push.rocks scope
|
||||||
// pushrocks scope
|
|
||||||
import * as projectinfo from '@push.rocks/projectinfo';
|
import * as projectinfo from '@push.rocks/projectinfo';
|
||||||
import * as qenv from '@push.rocks/qenv';
|
import * as qenv from '@push.rocks/qenv';
|
||||||
import * as smartdata from '@push.rocks/smartdata';
|
import * as smartdata from '@push.rocks/smartdata';
|
||||||
|
2
assets/templates/tsconfig_update/.smartscaf.yml
Normal file
2
assets/templates/tsconfig_update/.smartscaf.yml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
runafter:
|
||||||
|
- gitzone format
|
@@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"experimentalDecorators": true,
|
"experimentalDecorators": true,
|
||||||
|
"emitDecoratorMetadata": true,
|
||||||
"useDefineForClassFields": false,
|
"useDefineForClassFields": false,
|
||||||
"target": "ES2022",
|
"target": "ES2022",
|
||||||
"module": "NodeNext",
|
"module": "NodeNext",
|
||||||
|
2
assets/templates/wcc/ts_web/pages/index.ts
Normal file
2
assets/templates/wcc/ts_web/pages/index.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
export * from './page2.js';
|
||||||
|
export * from './page1.js';
|
5
assets/templates/wcc/ts_web/pages/page1.ts
Normal file
5
assets/templates/wcc/ts_web/pages/page1.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { html } from "@design.estate/dees-element";
|
||||||
|
|
||||||
|
export const page1 = () => html`
|
||||||
|
<first-element></first-element>
|
||||||
|
`;
|
5
assets/templates/wcc/ts_web/pages/page2.ts
Normal file
5
assets/templates/wcc/ts_web/pages/page2.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { html } from "@design.estate/dees-element";
|
||||||
|
|
||||||
|
export const page2 = () => html`
|
||||||
|
<first-element></first-element>
|
||||||
|
`;
|
@@ -6,7 +6,7 @@ import {
|
|||||||
cssManager,
|
cssManager,
|
||||||
unsafeCSS,
|
unsafeCSS,
|
||||||
css,
|
css,
|
||||||
TemplateResult,
|
type TemplateResult,
|
||||||
} from '@design.estate/dees-element';
|
} from '@design.estate/dees-element';
|
||||||
|
|
||||||
@customElement('default-header')
|
@customElement('default-header')
|
||||||
|
234
changelog.md
234
changelog.md
@@ -1,5 +1,239 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 2025-08-08 - 1.16.8 - fix(format)
|
||||||
|
Improve concurrency control in cache and rollback management with mutex locking and refine formatting details
|
||||||
|
|
||||||
|
- Added 'withMutex' functions in ChangeCache and RollbackManager to synchronize file I/O operations
|
||||||
|
- Introduced static mutex maps to prevent race conditions during manifest updates
|
||||||
|
- Fixed minor formatting issues in commit info and package.json
|
||||||
|
|
||||||
|
## 2025-08-08 - 1.16.7 - fix(core)
|
||||||
|
Improve formatting, logging, and rollback integrity in core modules
|
||||||
|
|
||||||
|
- Add .claude/settings.local.json with defined permissions for allowed commands
|
||||||
|
- Standardize formatting in package.json, commit info, and configuration files
|
||||||
|
- Refactor rollback manager to use atomic manifest writes and validate manifest structure
|
||||||
|
- Enhance logging messages and overall code clarity in CLI and commit modules
|
||||||
|
|
||||||
|
## 2025-08-08 - 1.16.6 - fix(changecache)
|
||||||
|
Improve cache manifest validation and atomic file writes; add local settings and overrides
|
||||||
|
|
||||||
|
- Add manifest structure validation and default fallback in getManifest
|
||||||
|
- Implement atomic write in saveManifest using a temporary file and rename strategy
|
||||||
|
- Enhance error handling and cleanup for corrupted manifest files
|
||||||
|
- Introduce new .claude/settings.local.json for project-specific permission configuration
|
||||||
|
- Add an empty assets/overrides.json file for future overrides
|
||||||
|
|
||||||
|
## 2025-08-08 - 1.16.5 - fix(prettier)
|
||||||
|
Improve file selection in Prettier formatter, remove legacy package overrides, and update CI template indentation
|
||||||
|
|
||||||
|
- Added .claude/settings.local.json with updated permission settings for local commands
|
||||||
|
- Removed unnecessary overrides from assets/overrides.json and cleared packageManager overrides in package.json
|
||||||
|
- Adjusted CI template files (ci_default_gitlab, ci_default_private_gitlab, ci_docker_gitlab) for consistent indentation and formatting
|
||||||
|
- Refined Prettier formatter logic by defining include directories, root config files, and filtering duplicates instead of manual exclusion
|
||||||
|
|
||||||
|
## 2025-08-08 - 1.16.4 - fix(prettier)
|
||||||
|
Improve file exclusion in the Prettier formatter to skip unnecessary files and directories.
|
||||||
|
|
||||||
|
- Added exclusion patterns for node_modules, .git, dist, .nogit, coverage, .nyc_output, vendor, bower_components, jspm_packages, and minified files.
|
||||||
|
- Optimized filtering logic to ensure only valid files are processed.
|
||||||
|
|
||||||
|
## 2025-08-08 - 1.16.3 - fix(changecache/prettier)
|
||||||
|
Skip directories during file processing to prevent errors in changecache and prettier formatting
|
||||||
|
|
||||||
|
- Removed unnecessary await on synchronous file reads in changecache
|
||||||
|
- Added directory checks in changecache to immediately skip directories
|
||||||
|
- Filtered out directories in prettier formatter to avoid processing non-files
|
||||||
|
|
||||||
|
## 2025-08-07 - 1.16.2 - fix(format)
|
||||||
|
Fix format command confirmation prompt to correctly check user response
|
||||||
|
|
||||||
|
- Fixed bug where format command always showed "cancelled" even when user confirmed
|
||||||
|
- Changed response check from `response.proceed` to `response.value` for SmartInteract compatibility
|
||||||
|
|
||||||
|
## 2025-08-04 - 1.16.1 - fix(package/config)
|
||||||
|
Move smartdiff dependency to runtime and add local bash permissions settings
|
||||||
|
|
||||||
|
- Moved '@push.rocks/smartdiff' from devDependencies to dependencies in package.json
|
||||||
|
- Added .claude/settings.local.json with allowed bash commands (grep, mkdir, find, ls)
|
||||||
|
|
||||||
|
## 2025-05-19 - 1.16.0 - feat(format)
|
||||||
|
Enhance format module with rollback, diff reporting, and improved parallel execution
|
||||||
|
|
||||||
|
- Implemented rollback functionality with backup management and automatic rollback on error
|
||||||
|
- Added CLI commands for rollback, listing backups, and cleaning old backups
|
||||||
|
- Introduced DiffReporter for generating and displaying file diffs
|
||||||
|
- Improved file change caching via ChangeCache and expanded dependency analysis for parallel execution
|
||||||
|
- Updated logging to support verbose mode and enhanced user feedback
|
||||||
|
- Updated package.json to include new dependency '@push.rocks/smartdiff'
|
||||||
|
|
||||||
|
## 2025-05-14 - 1.15.5 - fix(dependencies)
|
||||||
|
Update @git.zone/tsdoc to ^1.5.0 and @types/node to ^22.15.18
|
||||||
|
|
||||||
|
- Bumped @git.zone/tsdoc from ^1.4.5 to ^1.5.0
|
||||||
|
- Bumped @types/node from ^22.15.17 to ^22.15.18
|
||||||
|
|
||||||
|
## 2025-05-13 - 1.15.4 - fix(package.json)
|
||||||
|
Update dependency versions: bump @git.zone/tsdoc, @push.rocks/lik, @push.rocks/smartlog, and @types/node to their latest releases
|
||||||
|
|
||||||
|
- Upgrade @git.zone/tsdoc from ^1.4.4 to ^1.4.5
|
||||||
|
- Upgrade @push.rocks/lik from ^6.0.15 to ^6.2.2
|
||||||
|
- Upgrade @push.rocks/smartlog from ^3.0.7 to ^3.0.9
|
||||||
|
- Upgrade @types/node from ^22.14.1 to ^22.15.17
|
||||||
|
|
||||||
|
## 2025-04-15 - 1.15.3 - fix(deps)
|
||||||
|
update dependency versions and improve website template variable handling
|
||||||
|
|
||||||
|
- Bumped @git.zone/tsbuild from ^2.2.1 to ^2.3.2 and @types/node to ^22.14.1
|
||||||
|
- Upgraded @push.rocks/smartscaf from ^4.0.15 to ^4.0.16 and prettier from ^3.5.2 to ^3.5.3
|
||||||
|
- Refactored website template update to correctly supply variables with added logging
|
||||||
|
|
||||||
|
## 2025-04-15 - 1.15.2 - fix(website_update)
|
||||||
|
Await supplyVariables call in website update template
|
||||||
|
|
||||||
|
- Changed website template update to properly await the supplyVariables method
|
||||||
|
- Ensured asynchronous consistency in updating website template variables
|
||||||
|
|
||||||
|
## 2025-04-15 - 1.15.1 - fix(cli)
|
||||||
|
Refresh internal CLI tooling and configuration for consistency.
|
||||||
|
|
||||||
|
|
||||||
|
## 2025-04-15 - 1.15.0 - feat(config/template)
|
||||||
|
Add assetbrokerUrl and legalUrl fields to module config and update website template to supply these values
|
||||||
|
|
||||||
|
- Added assetbrokerUrl and legalUrl properties in ts/classes.gitzoneconfig.ts
|
||||||
|
- Updated ts/mod_format/format.templates.ts to pass assetbrokerUrl and legalUrl to website template
|
||||||
|
|
||||||
|
## 2025-04-15 - 1.14.1 - fix(package.json)
|
||||||
|
Add packageManager field to specify pnpm version for consistent package management
|
||||||
|
|
||||||
|
- Inserted packageManager property in package.json with pnpm version info to ensure reproducible dependency installs
|
||||||
|
|
||||||
|
## 2025-04-15 - 1.14.0 - feat(tsconfig_update)
|
||||||
|
Add runafter directive to trigger gitzone format after tsconfig update
|
||||||
|
|
||||||
|
- Added runafter configuration in assets/templates/tsconfig_update/.smartscaf.yml to automate formatting task
|
||||||
|
|
||||||
|
## 2025-03-07 - 1.13.1 - fix(cli)
|
||||||
|
Improve commit message logging
|
||||||
|
|
||||||
|
- Updated logging to display recommended next commit details.
|
||||||
|
- Enabled interactive prompt for choosing commit type and scope.
|
||||||
|
|
||||||
|
## 2025-02-28 - 1.13.0 - feat(templates)
|
||||||
|
Updated and added new TypeScript template files for npm projects
|
||||||
|
|
||||||
|
- Added new paths.ts and plugins.ts template files for npm projects.
|
||||||
|
- Removed outdated some.plugins.ts template file.
|
||||||
|
|
||||||
|
## 2025-02-25 - 1.12.8 - fix(metadata)
|
||||||
|
Updated package and npmextra json description and keywords for enhanced development workflow clarity
|
||||||
|
|
||||||
|
- Updated the description in package.json to focus on project setup and management.
|
||||||
|
- Aligned the keywords in both package.json and npmextra.json to include more relevant terms such as gitzone utilities, template management, and CI/CD.
|
||||||
|
|
||||||
|
## 2025-02-25 - 1.12.7 - fix(meta)
|
||||||
|
Fix issues in project metadata and configuration.
|
||||||
|
|
||||||
|
- Updated package metadata to ensure accurate project description and licensing.
|
||||||
|
- Ensured npm access level configuration consistency within npmextra.json.
|
||||||
|
|
||||||
|
## 2025-02-25 - 1.12.7 - fix(ci)
|
||||||
|
Updated dependencies and added CI/CD workflows.
|
||||||
|
|
||||||
|
- Updated several dependencies in package.json for compatibility and security.
|
||||||
|
- Added default GitHub and GitLab CI/CD workflow files to the test directory.
|
||||||
|
- Introduced a .gitignore file to exclude certain directories and files.
|
||||||
|
- Ensured consistent formatting with Prettier and TypeScript configurations.
|
||||||
|
|
||||||
|
## 2025-01-29 - 1.12.6 - fix(project)
|
||||||
|
Minor fixes and cleanup
|
||||||
|
|
||||||
|
- Removed outdated pages/ directory entry in .gitignore.
|
||||||
|
- Adjusted Docker module files to include proper newlines.
|
||||||
|
- Updated TypeScript configuration for modular paths.
|
||||||
|
- Fixed formatting issues across various TypeScript files.
|
||||||
|
|
||||||
|
## 2025-01-29 - 1.12.5 - fix(cli)
|
||||||
|
Initial implementation of CLI utility with project management features
|
||||||
|
|
||||||
|
- Integration of various plugins for logging, command-line interactions, and project management.
|
||||||
|
- Setup CLI commands for project initialization, updating, formatting, and more.
|
||||||
|
- Implement commands for packaging, versioning, and deprecating npm packages.
|
||||||
|
|
||||||
|
## 2025-01-29 - 1.12.2 - fix(format)
|
||||||
|
Add overrides for peek-readable in package.json formatting
|
||||||
|
|
||||||
|
- Added a URL correction in the packageJson repository information.
|
||||||
|
- Introduced support for pnpm overrides by including an `overrides.json` file.
|
||||||
|
|
||||||
|
## 2025-01-18 - 1.12.1 - fix(dependencies)
|
||||||
|
Update various package dependencies and Dockerfile base image
|
||||||
|
|
||||||
|
- Updated Dockerfile base image from 'alpinenpmci' to 'alpine_npmci'.
|
||||||
|
- Upgraded @git.zone/tsbuild, @git.zone/tsrun, @git.zone/tsdoc, and other dependencies to their latest versions.
|
||||||
|
|
||||||
|
## 2025-01-17 - 1.12.0 - feat(build)
|
||||||
|
Update TypeScript configuration to support emit decorator metadata
|
||||||
|
|
||||||
|
- Added emitDecoratorMetadata to the tsconfig.json template in assets/templates/tsconfig_update.
|
||||||
|
|
||||||
|
## 2025-01-08 - 1.11.0 - feat(cli)
|
||||||
|
Add Docker command for cleaning up Docker system and extend deprecation command for multiple registries
|
||||||
|
|
||||||
|
- Added a new command 'docker' to handle Docker system cleanup operations.
|
||||||
|
- Improved the 'deprecate' command to support deprecating packages across multiple npm registry URLs.
|
||||||
|
|
||||||
|
## 2025-01-01 - 1.10.10 - fix(templates)
|
||||||
|
Corrected typo in template file comment
|
||||||
|
|
||||||
|
- Fixed repeated comment in the template file for services under 'assets/templates/service/ts/some.plugins.ts'.
|
||||||
|
|
||||||
|
## 2025-01-01 - 1.10.9 - fix(templates)
|
||||||
|
Correct template file paths and organization for service projects
|
||||||
|
|
||||||
|
- Moved 'some.classes.some.ts' to 'classes.some.ts'
|
||||||
|
- Moved 'some.classes.some.db.ts' to 'classes.some.db.ts'
|
||||||
|
- Resolved incorrect import paths in service templates
|
||||||
|
|
||||||
|
## 2025-01-01 - 1.10.8 - fix(assets/templates)
|
||||||
|
Update CI template configurations to use module.githost
|
||||||
|
|
||||||
|
- Replaced occurrences of {{git.host}} with {{module.githost}} in CI workflow files
|
||||||
|
- Updated package dependencies for service template
|
||||||
|
|
||||||
|
## 2024-12-26 - 1.10.7 - fix(assets)
|
||||||
|
Correct URLs in templates and fix TypeScript declaration
|
||||||
|
|
||||||
|
- Updated incorrect URLs in Dockerfile templates to 'host.today'.
|
||||||
|
- Fixed type declaration for 'TemplateResult' in header.ts file.
|
||||||
|
|
||||||
|
## 2024-12-08 - 1.10.6 - fix(ci)
|
||||||
|
Corrected Docker image URL in CI templates
|
||||||
|
|
||||||
|
- Updated Docker image URL from 'code.foss.global/hosttoday' to 'code.foss.global/host.today' in default_nottags.yaml and default_tags.yaml.
|
||||||
|
- Adjusted gitignore template to include a custom section delineation.
|
||||||
|
|
||||||
|
## 2024-12-02 - 1.10.5 - fix(assets)
|
||||||
|
Update .gitignore template to remove pages directory
|
||||||
|
|
||||||
|
- Removed 'pages/' from the ignored directories in the .gitignore template.
|
||||||
|
|
||||||
|
## 2024-11-05 - 1.10.4 - fix(mod_format)
|
||||||
|
Correct file extension for TypeScript path configuration
|
||||||
|
|
||||||
|
- Fixed the TypeScript configuration to use correct file extensions for module subdirectories.
|
||||||
|
|
||||||
|
## 2024-10-27 - 1.10.3 - fix(mod_format)
|
||||||
|
Reorder TypeScript formatting steps in mod_format module
|
||||||
|
|
||||||
|
- Moved TypeScript configuration formatting earlier in the sequence for better logical consistency.
|
||||||
|
|
||||||
|
## 2024-10-27 - 1.10.2 - fix(format)
|
||||||
|
Add logging for tsconfig.json formatting
|
||||||
|
|
||||||
|
- Added an info log message for tsconfig.json formatting in format.tsconfig.ts.
|
||||||
|
|
||||||
## 2024-10-27 - 1.10.1 - fix(format)
|
## 2024-10-27 - 1.10.1 - fix(format)
|
||||||
Fixed async issue in tsconfig module lookup and corrected property access
|
Fixed async issue in tsconfig module lookup and corrected property access
|
||||||
|
|
||||||
|
@@ -10,21 +10,26 @@
|
|||||||
"githost": "gitlab.com",
|
"githost": "gitlab.com",
|
||||||
"gitscope": "gitzone/private",
|
"gitscope": "gitzone/private",
|
||||||
"gitrepo": "gitzone",
|
"gitrepo": "gitzone",
|
||||||
"description": "A CLI toolbelt to streamline local development cycles by using various gitzone utilities.",
|
"description": "A comprehensive CLI tool for enhancing and managing local development workflows with gitzone utilities, focusing on project setup, version control, code formatting, and template management.",
|
||||||
"npmPackagename": "@gitzone_private/gitzone",
|
"npmPackagename": "@gitzone_private/gitzone",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"CLI",
|
"CLI",
|
||||||
"development",
|
"local development",
|
||||||
"git",
|
"gitzone utilities",
|
||||||
"npm",
|
"project setup",
|
||||||
|
"version control",
|
||||||
|
"code formatting",
|
||||||
|
"template management",
|
||||||
|
"npm packages",
|
||||||
"TypeScript",
|
"TypeScript",
|
||||||
"automation",
|
"automation",
|
||||||
"project setup",
|
"commit management",
|
||||||
"code formatting",
|
|
||||||
"template creation",
|
"template creation",
|
||||||
"logging",
|
"meta project management",
|
||||||
"meta project management"
|
"sync",
|
||||||
|
"Docker",
|
||||||
|
"CI/CD"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
66
package.json
66
package.json
@@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"name": "@git.zone/cli",
|
"name": "@git.zone/cli",
|
||||||
"private": false,
|
"private": false,
|
||||||
"version": "1.10.1",
|
"version": "1.16.8",
|
||||||
"description": "A CLI toolbelt to streamline local development cycles by using various gitzone utilities.",
|
"description": "A comprehensive CLI tool for enhancing and managing local development workflows with gitzone utilities, focusing on project setup, version control, code formatting, and template management.",
|
||||||
"main": "dist_ts/index.ts",
|
"main": "dist_ts/index.ts",
|
||||||
"typings": "dist_ts/index.d.ts",
|
"typings": "dist_ts/index.d.ts",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
@@ -30,65 +30,71 @@
|
|||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://code.foss.global/git.zone/cli.git"
|
"url": "https://gitlab.com/gitzone/private/gitzone.git"
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"CLI",
|
"CLI",
|
||||||
"development",
|
"local development",
|
||||||
"git",
|
"gitzone utilities",
|
||||||
"npm",
|
"project setup",
|
||||||
|
"version control",
|
||||||
|
"code formatting",
|
||||||
|
"template management",
|
||||||
|
"npm packages",
|
||||||
"TypeScript",
|
"TypeScript",
|
||||||
"automation",
|
"automation",
|
||||||
"project setup",
|
"commit management",
|
||||||
"code formatting",
|
|
||||||
"template creation",
|
"template creation",
|
||||||
"logging",
|
"meta project management",
|
||||||
"meta project management"
|
"sync",
|
||||||
|
"Docker",
|
||||||
|
"CI/CD"
|
||||||
],
|
],
|
||||||
"author": "Task Venture Capital GmbH",
|
"author": "Task Venture Capital GmbH",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://code.foss.global/git.zone/cli/issues"
|
"url": "https://gitlab.com/gitzone/private/gitzone/issues"
|
||||||
},
|
},
|
||||||
"homepage": "https://pages.foss.global/git.zone/cli",
|
"homepage": "https://gitlab.com/gitzone/private/gitzone#readme",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@git.zone/tsbuild": "^2.1.84",
|
"@git.zone/tsbuild": "^2.3.2",
|
||||||
"@git.zone/tsrun": "^1.2.49",
|
"@git.zone/tsrun": "^1.3.3",
|
||||||
"@git.zone/tstest": "^1.0.90",
|
"@git.zone/tstest": "^1.0.96",
|
||||||
"@types/node": "^22.8.1"
|
"@types/node": "^22.15.18"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@git.zone/tsdoc": "^1.3.12",
|
"@git.zone/tsdoc": "^1.5.0",
|
||||||
"@git.zone/tspublish": "^1.4.0",
|
"@git.zone/tspublish": "^1.9.1",
|
||||||
"@push.rocks/commitinfo": "^1.0.12",
|
"@push.rocks/commitinfo": "^1.0.12",
|
||||||
"@push.rocks/early": "^4.0.4",
|
"@push.rocks/early": "^4.0.4",
|
||||||
"@push.rocks/gulp-function": "^3.0.7",
|
"@push.rocks/gulp-function": "^3.0.7",
|
||||||
"@push.rocks/lik": "^6.0.15",
|
"@push.rocks/lik": "^6.2.2",
|
||||||
"@push.rocks/npmextra": "^5.0.23",
|
"@push.rocks/npmextra": "^5.1.2",
|
||||||
"@push.rocks/projectinfo": "^5.0.2",
|
"@push.rocks/projectinfo": "^5.0.2",
|
||||||
"@push.rocks/smartchok": "^1.0.34",
|
"@push.rocks/smartchok": "^1.0.34",
|
||||||
"@push.rocks/smartcli": "^4.0.11",
|
"@push.rocks/smartcli": "^4.0.11",
|
||||||
"@push.rocks/smartdelay": "^3.0.5",
|
"@push.rocks/smartdelay": "^3.0.5",
|
||||||
"@push.rocks/smartfile": "^11.0.21",
|
"@push.rocks/smartdiff": "^1.0.3",
|
||||||
|
"@push.rocks/smartfile": "^11.2.0",
|
||||||
"@push.rocks/smartgulp": "^3.0.4",
|
"@push.rocks/smartgulp": "^3.0.4",
|
||||||
"@push.rocks/smartinteract": "^2.0.15",
|
"@push.rocks/smartinteract": "^2.0.15",
|
||||||
"@push.rocks/smartjson": "^5.0.20",
|
"@push.rocks/smartjson": "^5.0.20",
|
||||||
"@push.rocks/smartlegal": "^1.0.27",
|
"@push.rocks/smartlegal": "^1.0.27",
|
||||||
"@push.rocks/smartlog": "^3.0.7",
|
"@push.rocks/smartlog": "^3.0.9",
|
||||||
"@push.rocks/smartlog-destination-local": "^9.0.2",
|
"@push.rocks/smartlog-destination-local": "^9.0.2",
|
||||||
"@push.rocks/smartmustache": "^3.0.2",
|
"@push.rocks/smartmustache": "^3.0.2",
|
||||||
"@push.rocks/smartnpm": "^2.0.4",
|
"@push.rocks/smartnpm": "^2.0.4",
|
||||||
"@push.rocks/smartobject": "^1.0.12",
|
"@push.rocks/smartobject": "^1.0.12",
|
||||||
"@push.rocks/smartopen": "^2.0.0",
|
"@push.rocks/smartopen": "^2.0.0",
|
||||||
"@push.rocks/smartpath": "^5.0.18",
|
"@push.rocks/smartpath": "^5.0.18",
|
||||||
"@push.rocks/smartpromise": "^4.0.4",
|
"@push.rocks/smartpromise": "^4.2.3",
|
||||||
"@push.rocks/smartscaf": "^4.0.15",
|
"@push.rocks/smartscaf": "^4.0.16",
|
||||||
"@push.rocks/smartshell": "^3.0.6",
|
"@push.rocks/smartshell": "^3.2.3",
|
||||||
"@push.rocks/smartstream": "^3.0.44",
|
"@push.rocks/smartstream": "^3.2.5",
|
||||||
"@push.rocks/smartunique": "^3.0.9",
|
"@push.rocks/smartunique": "^3.0.9",
|
||||||
"@push.rocks/smartupdate": "^2.0.6",
|
"@push.rocks/smartupdate": "^2.0.6",
|
||||||
"@types/through2": "^2.0.41",
|
"@types/through2": "^2.0.41",
|
||||||
"prettier": "^3.3.3",
|
"prettier": "^3.5.3",
|
||||||
"through2": "^4.0.2"
|
"through2": "^4.0.2"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
@@ -105,5 +111,9 @@
|
|||||||
],
|
],
|
||||||
"browserslist": [
|
"browserslist": [
|
||||||
"last 1 chrome versions"
|
"last 1 chrome versions"
|
||||||
]
|
],
|
||||||
|
"pnpm": {
|
||||||
|
"overrides": {}
|
||||||
|
},
|
||||||
|
"packageManager": "pnpm@10.7.0+sha512.6b865ad4b62a1d9842b61d674a393903b871d9244954f652b8842c2b553c72176b278f64c463e52d40fff8aba385c235c8c9ecf5cc7de4fd78b8bb6d49633ab6"
|
||||||
}
|
}
|
||||||
|
12043
pnpm-lock.yaml
generated
12043
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
187
readme.hints.md
187
readme.hints.md
@@ -1 +1,188 @@
|
|||||||
|
# Gitzone CLI - Development Hints
|
||||||
|
|
||||||
* the cli of the git.zone project.
|
* the cli of the git.zone project.
|
||||||
|
|
||||||
|
## Project Overview
|
||||||
|
|
||||||
|
Gitzone CLI (`@git.zone/cli`) is a comprehensive toolbelt for streamlining local development cycles. It provides utilities for:
|
||||||
|
- Project initialization and templating (via smartscaf)
|
||||||
|
- Code formatting and standardization
|
||||||
|
- Version control and commit management
|
||||||
|
- Docker and CI/CD integration
|
||||||
|
- Meta project management
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
### Core Structure
|
||||||
|
- Main CLI entry: `cli.ts` / `cli.child.ts`
|
||||||
|
- Modular architecture with separate modules in `ts/mod_*` directories
|
||||||
|
- Each module handles specific functionality (format, commit, docker, etc.)
|
||||||
|
- Extensive use of plugins pattern via `plugins.ts` files
|
||||||
|
|
||||||
|
### Configuration Management
|
||||||
|
- Uses `npmextra.json` for all tool configuration
|
||||||
|
- Configuration stored under `gitzone` key in npmextra
|
||||||
|
- No separate `.gitzonerc` file - everything in npmextra.json
|
||||||
|
- Project type and module metadata also stored in npmextra
|
||||||
|
|
||||||
|
### Format Module (`mod_format`) - SIGNIFICANTLY ENHANCED
|
||||||
|
|
||||||
|
The format module is responsible for project standardization:
|
||||||
|
|
||||||
|
#### Current Modules:
|
||||||
|
1. **cleanup** - Removes obsolete files (yarn.lock, tslint.json, etc.)
|
||||||
|
2. **copy** - File copying with glob patterns (fully implemented)
|
||||||
|
3. **gitignore** - Creates/updates .gitignore from templates
|
||||||
|
4. **license** - Checks dependency licenses for compatibility
|
||||||
|
5. **npmextra** - Manages project metadata and configuration
|
||||||
|
6. **packagejson** - Formats and updates package.json
|
||||||
|
7. **prettier** - Applies code formatting with batching
|
||||||
|
8. **readme** - Ensures readme files exist
|
||||||
|
9. **templates** - Updates project templates based on type
|
||||||
|
10. **tsconfig** - Formats TypeScript configuration
|
||||||
|
|
||||||
|
#### Execution Order (Dependency-Based):
|
||||||
|
- Modules are now executed in parallel groups based on dependencies
|
||||||
|
- Independent modules run concurrently for better performance
|
||||||
|
- Dependency analyzer ensures correct execution order
|
||||||
|
|
||||||
|
### New Architecture Features
|
||||||
|
|
||||||
|
1. **BaseFormatter Pattern**: All formatters extend abstract BaseFormatter class
|
||||||
|
2. **FormatContext**: Central state management across all modules
|
||||||
|
3. **FormatPlanner**: Implements plan → action workflow
|
||||||
|
4. **RollbackManager**: Full backup/restore capabilities
|
||||||
|
5. **ChangeCache**: Tracks file changes to optimize performance
|
||||||
|
6. **DependencyAnalyzer**: Manages module execution order
|
||||||
|
7. **DiffReporter**: Generates diff views for changes
|
||||||
|
8. **FormatStats**: Comprehensive execution statistics
|
||||||
|
|
||||||
|
### Key Patterns
|
||||||
|
|
||||||
|
1. **Plugin Architecture**: All dependencies imported through `plugins.ts` files
|
||||||
|
2. **Streaming**: Uses smartstream for file processing
|
||||||
|
3. **Interactive Prompts**: smartinteract for user input
|
||||||
|
4. **Enhanced Error Handling**: Comprehensive try-catch with automatic rollback
|
||||||
|
5. **Template System**: Templates handled by smartscaf, not directly by gitzone
|
||||||
|
6. **Type Safety**: Full TypeScript with interfaces and type definitions
|
||||||
|
|
||||||
|
### Important Notes
|
||||||
|
|
||||||
|
- `.nogit/` directory used for temporary/untracked files, backups, and cache
|
||||||
|
- `.nogit/gitzone-backups/` stores format operation backups
|
||||||
|
- `.nogit/gitzone-cache/` stores file change cache
|
||||||
|
- Templates are managed by smartscaf - improvements should be made there
|
||||||
|
- License checking configurable with exceptions support
|
||||||
|
- All features implemented: `ensureDependency`, copy module, etc.
|
||||||
|
|
||||||
|
## Recent Improvements (Completed)
|
||||||
|
|
||||||
|
1. **Plan → Action Workflow**: Shows changes before applying them
|
||||||
|
2. **Rollback Mechanism**: Full backup and restore on failures
|
||||||
|
3. **Enhanced Configuration**: Granular control via npmextra.json
|
||||||
|
4. **Better Error Handling**: Detailed errors with recovery options
|
||||||
|
5. **Performance Optimizations**: Parallel execution and caching
|
||||||
|
6. **Reporting**: Diff views, statistics, verbose logging
|
||||||
|
7. **Architecture**: Clean separation of concerns with new classes
|
||||||
|
|
||||||
|
## Development Tips
|
||||||
|
|
||||||
|
- Always check readme.plan.md for ongoing improvement plans
|
||||||
|
- Use npmextra.json for any new configuration options
|
||||||
|
- Keep modules focused and single-purpose
|
||||||
|
- Maintain the existing plugin pattern for dependencies
|
||||||
|
- Test format operations on sample projects before deploying
|
||||||
|
- Consider backward compatibility when changing configuration structure
|
||||||
|
- Use BaseFormatter pattern for new format modules
|
||||||
|
- Leverage FormatContext for cross-module state sharing
|
||||||
|
|
||||||
|
## Configuration Examples
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"gitzone": {
|
||||||
|
"format": {
|
||||||
|
"interactive": true,
|
||||||
|
"parallel": true,
|
||||||
|
"showStats": true,
|
||||||
|
"cache": {
|
||||||
|
"enabled": true,
|
||||||
|
"clean": true
|
||||||
|
},
|
||||||
|
"rollback": {
|
||||||
|
"enabled": true,
|
||||||
|
"autoRollbackOnError": true,
|
||||||
|
"backupRetentionDays": 7
|
||||||
|
},
|
||||||
|
"modules": {
|
||||||
|
"skip": ["prettier"],
|
||||||
|
"only": [],
|
||||||
|
"order": []
|
||||||
|
},
|
||||||
|
"licenses": {
|
||||||
|
"allowed": ["MIT", "Apache-2.0"],
|
||||||
|
"exceptions": {
|
||||||
|
"some-package": "GPL-3.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## CLI Usage
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Basic format
|
||||||
|
gitzone format
|
||||||
|
|
||||||
|
# Dry run to preview changes
|
||||||
|
gitzone format --dry-run
|
||||||
|
|
||||||
|
# Non-interactive mode
|
||||||
|
gitzone format --yes
|
||||||
|
|
||||||
|
# Plan only (no execution)
|
||||||
|
gitzone format --plan-only
|
||||||
|
|
||||||
|
# Save plan for later
|
||||||
|
gitzone format --save-plan format.json
|
||||||
|
|
||||||
|
# Execute saved plan
|
||||||
|
gitzone format --from-plan format.json
|
||||||
|
|
||||||
|
# Verbose mode
|
||||||
|
gitzone format --verbose
|
||||||
|
|
||||||
|
# Detailed diff views
|
||||||
|
gitzone format --detailed
|
||||||
|
|
||||||
|
# Rollback operations
|
||||||
|
gitzone format --rollback
|
||||||
|
gitzone format --rollback <operation-id>
|
||||||
|
gitzone format --list-backups
|
||||||
|
gitzone format --clean-backups
|
||||||
|
```
|
||||||
|
|
||||||
|
## Common Issues (Now Resolved)
|
||||||
|
|
||||||
|
1. ✅ Format operations are now reversible with rollback
|
||||||
|
2. ✅ Enhanced error messages with recovery suggestions
|
||||||
|
3. ✅ All modules fully implemented (including copy)
|
||||||
|
4. ✅ Dry-run capability available
|
||||||
|
5. ✅ Extensive configuration options available
|
||||||
|
|
||||||
|
## Future Considerations
|
||||||
|
|
||||||
|
- Plugin system for custom formatters
|
||||||
|
- Git hooks integration for pre-commit formatting
|
||||||
|
- Advanced UI with interactive configuration
|
||||||
|
- Format presets for common scenarios
|
||||||
|
- Performance benchmarking tools
|
||||||
|
|
||||||
|
## API Changes
|
||||||
|
|
||||||
|
- smartfile API updated to use fs.* and memory.* namespaces
|
||||||
|
- smartnpm requires instance creation: `new NpmRegistry()`
|
||||||
|
- All file operations now use updated APIs
|
||||||
|
- Type imports use `import type` for proper verbatim module syntax
|
469
readme.md
469
readme.md
@@ -1,191 +1,390 @@
|
|||||||
# @git.zone/cli
|
# @git.zone/cli 🚀
|
||||||
|
|
||||||
A CLI toolbelt to streamline local development cycles by using various gitzone utilities.
|
**The ultimate CLI toolbelt for modern TypeScript development workflows**
|
||||||
|
|
||||||
## Install
|
[](https://www.npmjs.com/package/@git.zone/cli)
|
||||||
|
[](https://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
To install the `@git.zone/cli` tool, you need to have Node.js and npm installed on your machine. Once you have those set up, you can install the CLI tool globally using the following command:
|
## 🎯 What is gitzone?
|
||||||
|
|
||||||
```shell
|
gitzone is a powerful command-line interface that supercharges your development workflow with automated project management, intelligent code formatting, and seamless version control. Whether you're bootstrapping a new TypeScript project, maintaining code quality, or managing complex multi-repository setups, gitzone has got you covered.
|
||||||
|
|
||||||
|
## 🏃♂️ Quick Start
|
||||||
|
|
||||||
|
### Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Install globally via npm
|
||||||
npm install -g @git.zone/cli
|
npm install -g @git.zone/cli
|
||||||
|
|
||||||
|
# Or with pnpm (recommended)
|
||||||
|
pnpm add -g @git.zone/cli
|
||||||
```
|
```
|
||||||
|
|
||||||
This will add the `gitzone` or `gzone` command to your PATH, allowing you to use the tool from any directory.
|
Once installed, you can use either `gitzone` or the shorter `gzone` command from anywhere in your terminal.
|
||||||
|
|
||||||
## Usage
|
### Your First Command
|
||||||
|
|
||||||
The `gitzone` CLI is designed to streamline various aspects of the local development cycle, including project setup, maintenance, and deployment. Below, we'll go through several scenarios that demonstrate the capabilities of `gitzone`.
|
```bash
|
||||||
|
# Create a new TypeScript npm package
|
||||||
|
gitzone template npm
|
||||||
|
|
||||||
### Getting Started
|
# Format your entire codebase
|
||||||
|
gitzone format
|
||||||
|
|
||||||
To start using `gitzone`, you need to initialize a new project or use it with an existing one. This section will guide you through the initial setup and provide examples of the core commands.
|
# Create a semantic commit
|
||||||
|
|
||||||
#### Initialize a New Project
|
|
||||||
|
|
||||||
`gitzone` can create several types of projects, including standard npm modules, websites using LitElement, and custom web components. To create a new project, you can use the following command:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
gitzone template [templatename]
|
|
||||||
```
|
|
||||||
|
|
||||||
Replace `[templatename]` with one of the following:
|
|
||||||
- `npm`: A standard npm module with TypeScript support, testing, and CI/CD setup.
|
|
||||||
- `website`: A LitElement-based website with e2e testing, bundling, and service worker support.
|
|
||||||
- `element`: A LitElement standard setup for creating web components.
|
|
||||||
|
|
||||||
Example of starting a new npm project:
|
|
||||||
1. Open your terminal and navigate to the directory where you want to create your project.
|
|
||||||
2. Execute the following command:
|
|
||||||
```shell
|
|
||||||
gitzone template npm
|
|
||||||
```
|
|
||||||
3. Follow the interactive prompts to set up your project. You will be asked to provide information such as the project name, description, GitHub repository, etc.
|
|
||||||
|
|
||||||
#### Committing Changes
|
|
||||||
|
|
||||||
To standardize commit messages and increment versions based on change types (fix, feat, breaking change):
|
|
||||||
|
|
||||||
```shell
|
|
||||||
gitzone commit
|
gitzone commit
|
||||||
```
|
```
|
||||||
|
|
||||||
This command will prompt you with a series of questions to help construct a standardized commit message and create a new commit.
|
## 🛠️ Core Features
|
||||||
|
|
||||||
#### Deprecating a Package
|
### 📦 Project Templates
|
||||||
|
|
||||||
To deprecate an old package in favor of a new one:
|
Instantly scaffold production-ready projects with best practices built-in:
|
||||||
|
|
||||||
```shell
|
```bash
|
||||||
|
gitzone template [template-name]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Available templates:**
|
||||||
|
- **`npm`** - TypeScript npm package with testing, CI/CD, and full tooling
|
||||||
|
- **`service`** - Microservice architecture with Docker support
|
||||||
|
- **`website`** - Modern web application with LitElement and service workers
|
||||||
|
- **`wcc`** - Web Component Collection for reusable UI components
|
||||||
|
|
||||||
|
Each template comes pre-configured with:
|
||||||
|
- ✅ TypeScript with modern configurations
|
||||||
|
- ✅ Automated testing setup
|
||||||
|
- ✅ CI/CD pipelines (GitLab/GitHub)
|
||||||
|
- ✅ Code formatting and linting
|
||||||
|
- ✅ Documentation structure
|
||||||
|
|
||||||
|
### 🎨 Intelligent Code Formatting
|
||||||
|
|
||||||
|
The most powerful feature of gitzone - automatically format and standardize your entire codebase:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Preview changes without applying them
|
||||||
|
gitzone format --dry-run
|
||||||
|
|
||||||
|
# Format with automatic approval
|
||||||
|
gitzone format --yes
|
||||||
|
|
||||||
|
# Save formatting plan for later execution
|
||||||
|
gitzone format --save-plan format-plan.json
|
||||||
|
|
||||||
|
# Execute a saved plan
|
||||||
|
gitzone format --from-plan format-plan.json
|
||||||
|
|
||||||
|
# Enable verbose output for debugging
|
||||||
|
gitzone format --verbose
|
||||||
|
```
|
||||||
|
|
||||||
|
**Format features:**
|
||||||
|
- 🔄 **Smart caching** - Only processes changed files
|
||||||
|
- 🛡️ **Rollback support** - Undo formatting changes if needed
|
||||||
|
- 📊 **Detailed reporting** - See exactly what changed
|
||||||
|
- ⚡ **Parallel execution** - Format multiple files simultaneously
|
||||||
|
- 🎯 **Module-specific formatting** - Target specific formatters
|
||||||
|
|
||||||
|
**Rollback capabilities:**
|
||||||
|
```bash
|
||||||
|
# List all available backups
|
||||||
|
gitzone format --list-backups
|
||||||
|
|
||||||
|
# Rollback to the last operation
|
||||||
|
gitzone format --rollback
|
||||||
|
|
||||||
|
# Rollback to a specific operation
|
||||||
|
gitzone format --rollback [operation-id]
|
||||||
|
|
||||||
|
# Clean old backups
|
||||||
|
gitzone format --clean-backups
|
||||||
|
```
|
||||||
|
|
||||||
|
**Formatters included:**
|
||||||
|
- **Prettier** - JavaScript/TypeScript code formatting
|
||||||
|
- **License** - Ensure proper licensing
|
||||||
|
- **Package.json** - Standardize package configurations
|
||||||
|
- **Tsconfig** - TypeScript configuration optimization
|
||||||
|
- **Readme** - Documentation formatting
|
||||||
|
- **Gitignore** - Repository ignore rules
|
||||||
|
- **Templates** - Project template updates
|
||||||
|
- **Npmextra** - Extended npm configurations
|
||||||
|
|
||||||
|
### 🔀 Semantic Commits & Versioning
|
||||||
|
|
||||||
|
Create standardized commits that automatically handle versioning:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
gitzone commit
|
||||||
|
```
|
||||||
|
|
||||||
|
Features:
|
||||||
|
- 📝 Interactive commit message builder
|
||||||
|
- 🏷️ Automatic version bumping (major/minor/patch)
|
||||||
|
- 📜 Changelog generation
|
||||||
|
- 🚀 Optional auto-push to origin
|
||||||
|
- 🎯 Conventional commit compliance
|
||||||
|
|
||||||
|
The commit wizard guides you through:
|
||||||
|
1. **Type selection** (feat/fix/docs/style/refactor/perf/test/chore)
|
||||||
|
2. **Scope definition** (component/module affected)
|
||||||
|
3. **Description crafting**
|
||||||
|
4. **Breaking change detection**
|
||||||
|
5. **Version bump determination**
|
||||||
|
|
||||||
|
### 🏗️ Meta Repository Management
|
||||||
|
|
||||||
|
Manage multiple related repositories as a cohesive unit:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Initialize a meta repository
|
||||||
|
gitzone meta init
|
||||||
|
|
||||||
|
# Add a sub-project
|
||||||
|
gitzone meta add [name] [git-url]
|
||||||
|
|
||||||
|
# Update all sub-projects
|
||||||
|
gitzone meta update
|
||||||
|
|
||||||
|
# Remove a sub-project
|
||||||
|
gitzone meta remove [name]
|
||||||
|
```
|
||||||
|
|
||||||
|
Perfect for:
|
||||||
|
- Monorepo management
|
||||||
|
- Multi-package projects
|
||||||
|
- Coordinated deployments
|
||||||
|
- Synchronized versioning
|
||||||
|
|
||||||
|
### 🐳 Docker Management
|
||||||
|
|
||||||
|
Streamline your Docker workflow:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Clean up all Docker resources
|
||||||
|
gitzone docker prune
|
||||||
|
```
|
||||||
|
|
||||||
|
This command removes:
|
||||||
|
- Stopped containers
|
||||||
|
- Unused images
|
||||||
|
- Dangling volumes
|
||||||
|
- Unused networks
|
||||||
|
|
||||||
|
### 🔗 Quick CI/CD Access
|
||||||
|
|
||||||
|
Jump directly to your CI/CD configurations:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Open CI/CD settings
|
||||||
|
gitzone open ci
|
||||||
|
|
||||||
|
# Open pipelines view
|
||||||
|
gitzone open pipelines
|
||||||
|
```
|
||||||
|
|
||||||
|
Works with GitLab repositories to provide instant access to your deployment configurations.
|
||||||
|
|
||||||
|
### 📝 Package Deprecation
|
||||||
|
|
||||||
|
Smoothly transition users from old to new packages:
|
||||||
|
|
||||||
|
```bash
|
||||||
gitzone deprecate
|
gitzone deprecate
|
||||||
```
|
```
|
||||||
|
|
||||||
This command will prompt you for the old and new package names and will automatically deprecate the old package on npm.
|
Interactive wizard for:
|
||||||
|
- Setting deprecation notices
|
||||||
|
- Guiding users to replacements
|
||||||
|
- Updating registry metadata
|
||||||
|
- Coordinating migration paths
|
||||||
|
|
||||||
#### Opening CI/CD Settings
|
### 🚦 Project Initialization
|
||||||
|
|
||||||
Quickly open the CI/CD settings page of your project repository:
|
Prepare existing projects for development:
|
||||||
|
|
||||||
```shell
|
```bash
|
||||||
gitzone open ci
|
gitzone start
|
||||||
```
|
```
|
||||||
|
|
||||||
### Example Commands
|
Automatically:
|
||||||
|
- Checks out master branch
|
||||||
|
- Pulls latest changes
|
||||||
|
- Installs dependencies
|
||||||
|
- Sets up development environment
|
||||||
|
|
||||||
#### Project Setup and Initialization
|
### 🔧 Helper Utilities
|
||||||
|
|
||||||
Let's go through how to scaffold a new web component project:
|
Quick utilities for common tasks:
|
||||||
|
|
||||||
1. Navigate to your desired directory.
|
```bash
|
||||||
2. Run the following command:
|
# Generate a unique short ID
|
||||||
```shell
|
gitzone helpers shortid
|
||||||
gitzone template element
|
```
|
||||||
```
|
|
||||||
3. Follow the prompts to set up your web component project.
|
|
||||||
|
|
||||||
#### Managing Project Lifecycle
|
## 📋 Configuration
|
||||||
|
|
||||||
Commands to manage your project's lifecycle:
|
### npmextra.json Configuration
|
||||||
|
|
||||||
- **Commit Changes:**
|
Customize gitzone behavior through `npmextra.json`:
|
||||||
```shell
|
|
||||||
gitzone commit
|
|
||||||
```
|
|
||||||
- **Deprecate a Package:**
|
|
||||||
```shell
|
|
||||||
gitzone deprecate
|
|
||||||
```
|
|
||||||
- **Format Project Files:**
|
|
||||||
```shell
|
|
||||||
gitzone format
|
|
||||||
```
|
|
||||||
- **Regenerate Readme:**
|
|
||||||
```shell
|
|
||||||
gitzone readme
|
|
||||||
```
|
|
||||||
- **Update Local Repositories:**
|
|
||||||
```shell
|
|
||||||
gitzone update
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Advanced Scenarios
|
```json
|
||||||
|
{
|
||||||
|
"gitzone": {
|
||||||
|
"format": {
|
||||||
|
"interactive": true,
|
||||||
|
"showDiffs": false,
|
||||||
|
"autoApprove": false,
|
||||||
|
"parallel": true,
|
||||||
|
"rollback": {
|
||||||
|
"enabled": true,
|
||||||
|
"autoRollbackOnError": true,
|
||||||
|
"backupRetentionDays": 7
|
||||||
|
},
|
||||||
|
"modules": {
|
||||||
|
"skip": ["prettier"],
|
||||||
|
"only": [],
|
||||||
|
"order": []
|
||||||
|
},
|
||||||
|
"cache": {
|
||||||
|
"enabled": true,
|
||||||
|
"clean": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
##### Formatting
|
### Environment Variables
|
||||||
|
|
||||||
To ensure consistent formatting using Prettier:
|
- `CI` - Detect CI environment for automated workflows
|
||||||
|
- `DEBUG` - Enable debug output
|
||||||
|
- `GITZONE_FORMAT_PARALLEL` - Control parallel formatting
|
||||||
|
|
||||||
```shell
|
## 🏆 Best Practices
|
||||||
|
|
||||||
|
### For New Projects
|
||||||
|
1. Start with a template: `gitzone template npm`
|
||||||
|
2. Customize the generated structure
|
||||||
|
3. Run initial format: `gitzone format`
|
||||||
|
4. Set up CI/CD: `gitzone open ci`
|
||||||
|
|
||||||
|
### For Existing Projects
|
||||||
|
1. Initialize: `gitzone start`
|
||||||
|
2. Format codebase: `gitzone format --dry-run` (preview first!)
|
||||||
|
3. Apply formatting: `gitzone format --yes`
|
||||||
|
4. Commit changes: `gitzone commit`
|
||||||
|
|
||||||
|
### For Teams
|
||||||
|
1. Document format preferences in `npmextra.json`
|
||||||
|
2. Use `--save-plan` for reviewable format changes
|
||||||
|
3. Enable rollback for safety
|
||||||
|
4. Standardize commit conventions
|
||||||
|
|
||||||
|
## 🎯 Common Workflows
|
||||||
|
|
||||||
|
### Clean Development Cycle
|
||||||
|
```bash
|
||||||
|
# 1. Start fresh
|
||||||
|
gitzone start
|
||||||
|
|
||||||
|
# 2. Make changes
|
||||||
|
# ... your development work ...
|
||||||
|
|
||||||
|
# 3. Format code
|
||||||
gitzone format
|
gitzone format
|
||||||
|
|
||||||
|
# 4. Commit with semantic versioning
|
||||||
|
gitzone commit
|
||||||
|
|
||||||
|
# 5. Deploy (if CI/CD configured)
|
||||||
|
# Automatic via git push
|
||||||
```
|
```
|
||||||
|
|
||||||
##### Building the Project
|
### Multi-Repository Management
|
||||||
|
```bash
|
||||||
To build your project as defined in your `package.json`:
|
# 1. Set up meta repository
|
||||||
|
|
||||||
```shell
|
|
||||||
npm run build
|
|
||||||
```
|
|
||||||
|
|
||||||
##### Git and Version Control
|
|
||||||
|
|
||||||
Check the current project version:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
gitzone -v
|
|
||||||
```
|
|
||||||
|
|
||||||
To synchronize local repositories with remotes:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
gitzone update
|
|
||||||
```
|
|
||||||
|
|
||||||
##### Metadata and Configuration
|
|
||||||
|
|
||||||
To initialize or update metadata:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
gitzone meta init
|
gitzone meta init
|
||||||
|
|
||||||
|
# 2. Add all related projects
|
||||||
|
gitzone meta add frontend https://github.com/org/frontend.git
|
||||||
|
gitzone meta add backend https://github.com/org/backend.git
|
||||||
|
gitzone meta add shared https://github.com/org/shared.git
|
||||||
|
|
||||||
|
# 3. Synchronize updates
|
||||||
|
gitzone meta update
|
||||||
```
|
```
|
||||||
|
|
||||||
### Continuous Integration and Delivery (CI/CD)
|
### Safe Formatting with Rollback
|
||||||
|
```bash
|
||||||
|
# 1. Preview changes
|
||||||
|
gitzone format --dry-run
|
||||||
|
|
||||||
#### Running Tests
|
# 2. Save plan for review
|
||||||
|
gitzone format --save-plan format-changes.json
|
||||||
|
|
||||||
To execute tests defined in your `package.json`:
|
# 3. Apply formatting
|
||||||
|
gitzone format --from-plan format-changes.json
|
||||||
|
|
||||||
```shell
|
# 4. If something goes wrong, rollback
|
||||||
npm test
|
gitzone format --rollback
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Building Documentation
|
## 🔌 Integrations
|
||||||
|
|
||||||
Generate documentation:
|
### CI/CD Platforms
|
||||||
|
- **GitLab CI** - Full pipeline support with templates
|
||||||
|
- **GitHub Actions** - Automated workflows
|
||||||
|
- **Docker** - Container-based deployments
|
||||||
|
|
||||||
```shell
|
### Development Tools
|
||||||
npm run buildDocs
|
- **TypeScript** - First-class support
|
||||||
|
- **Prettier** - Code formatting
|
||||||
|
- **ESLint** - Linting (via format modules)
|
||||||
|
- **npm/pnpm** - Package management
|
||||||
|
|
||||||
|
### Version Control
|
||||||
|
- **Git** - Deep integration
|
||||||
|
- **Semantic Versioning** - Automatic version bumping
|
||||||
|
- **Conventional Commits** - Standardized commit messages
|
||||||
|
|
||||||
|
## 💡 Pro Tips
|
||||||
|
|
||||||
|
1. **Use aliases**: Add `alias gz='gitzone'` to your shell profile
|
||||||
|
2. **Combine commands**: `gitzone format --yes && gitzone commit`
|
||||||
|
3. **Leverage templates**: Start projects right with proven structures
|
||||||
|
4. **Enable caching**: Dramatically speeds up formatting operations
|
||||||
|
5. **Save format plans**: Review changes before applying in production
|
||||||
|
|
||||||
|
## 🐛 Troubleshooting
|
||||||
|
|
||||||
|
### Format Command Shows "Cancelled"
|
||||||
|
If the format command shows cancelled even after confirming:
|
||||||
|
- Check your `npmextra.json` configuration
|
||||||
|
- Try with `--yes` flag to skip confirmation
|
||||||
|
- Use `--verbose` for detailed output
|
||||||
|
|
||||||
|
### Docker Commands Fail
|
||||||
|
Ensure Docker daemon is running:
|
||||||
|
```bash
|
||||||
|
docker info
|
||||||
```
|
```
|
||||||
|
|
||||||
### Troubleshooting and Debugging
|
### Template Creation Issues
|
||||||
|
Verify npm/pnpm is properly configured:
|
||||||
#### Detailed Logs
|
```bash
|
||||||
|
npm config get registry
|
||||||
Enable detailed logging for troubleshooting:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
gitzone --loglevel=debug
|
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Cleaning Up
|
## 📈 Performance
|
||||||
|
|
||||||
To clean up project artifacts:
|
gitzone is optimized for speed:
|
||||||
|
- **Parallel processing** for format operations
|
||||||
```shell
|
- **Smart caching** to avoid redundant work
|
||||||
gitzone clean
|
- **Incremental updates** for meta repositories
|
||||||
```
|
- **Minimal dependencies** for fast installation
|
||||||
|
|
||||||
### Summary
|
|
||||||
|
|
||||||
The `gitzone` CLI tool provides a comprehensive suite of commands that streamline project setup, lifecycle management, and deployment, which are indispensable for modern development workflows. By familiarizing yourself with the different commands, you can maximize your productivity and focus on what really matters—writing code. Whether you are starting a new project, maintaining an existing one, or deploying your work, `gitzone` is your toolbelt for efficient development cycles.
|
|
||||||
|
|
||||||
## License and Legal Information
|
## License and Legal Information
|
||||||
|
|
||||||
|
170
readme.plan.md
Normal file
170
readme.plan.md
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
# Gitzone Format Module Improvement Plan
|
||||||
|
|
||||||
|
Please reread /home/philkunz/.claude/CLAUDE.md before proceeding with any implementation.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
This plan outlines improvements for the gitzone format module to enhance its functionality, reliability, and maintainability.
|
||||||
|
|
||||||
|
## Phase 1: Core Improvements (High Priority) - COMPLETED ✅
|
||||||
|
|
||||||
|
### 1. Enhanced Error Handling & Recovery ✅
|
||||||
|
- [x] Implement rollback mechanism for failed format operations
|
||||||
|
- [x] Add detailed error messages with recovery suggestions
|
||||||
|
- [x] Create a `--dry-run` flag to preview changes before applying
|
||||||
|
- [x] Add transaction-like behavior: all-or-nothing formatting
|
||||||
|
- [x] Implement plan → action workflow as default behavior
|
||||||
|
|
||||||
|
### 2. Complete Missing Functionality ✅
|
||||||
|
- [x] Implement the `ensureDependency` function in format.packagejson.ts
|
||||||
|
- [x] Develop the copy module for file pattern-based copying
|
||||||
|
- [x] Add dependency version constraint management
|
||||||
|
- [x] Support workspace/monorepo configurations (via configuration)
|
||||||
|
|
||||||
|
### 3. Configuration & Flexibility ✅
|
||||||
|
- [x] Extend npmextra.json gitzone configuration section
|
||||||
|
- [x] Allow custom license exclusion/inclusion lists
|
||||||
|
- [x] Make format steps configurable (skip/include specific modules)
|
||||||
|
- [x] Support custom template directories (via configuration)
|
||||||
|
- [x] Add format profiles for different project types
|
||||||
|
|
||||||
|
### 4. Architecture Changes ✅
|
||||||
|
- [x] Introduce a `FormatContext` class to manage state across modules
|
||||||
|
- [x] Create abstract `BaseFormatter` class for consistent module structure
|
||||||
|
- [x] Implement event system for inter-module communication (via context)
|
||||||
|
- [x] Add validation layer before format execution
|
||||||
|
- [x] Implement `FormatPlanner` class for plan → action workflow
|
||||||
|
|
||||||
|
## Phase 2: Performance & Reporting (Medium Priority) - COMPLETED ✅
|
||||||
|
|
||||||
|
### 5. Performance Optimizations ✅
|
||||||
|
- [x] Implement parallel execution for independent format modules
|
||||||
|
- [x] Add file change detection to skip unchanged files
|
||||||
|
- [x] Create format cache to track last formatted state
|
||||||
|
- [x] Optimize Prettier runs by batching files
|
||||||
|
|
||||||
|
### 6. Enhanced Reporting & Visibility ✅
|
||||||
|
- [x] Generate comprehensive format report showing all changes
|
||||||
|
- [x] Add diff view for file modifications
|
||||||
|
- [x] Create verbose logging option
|
||||||
|
- [x] Add format statistics (files changed, time taken, etc.)
|
||||||
|
|
||||||
|
## Phase 3: Advanced Features (Lower Priority) - PARTIALLY COMPLETED
|
||||||
|
|
||||||
|
### 7. Better Integration & Extensibility ⏳
|
||||||
|
- [ ] Create plugin system for custom format modules
|
||||||
|
- [ ] Add hooks for pre/post format operations
|
||||||
|
- [ ] Support custom validation rules
|
||||||
|
- [ ] Integrate with git hooks for pre-commit formatting
|
||||||
|
|
||||||
|
### 8. Improved Template Integration ⏳
|
||||||
|
- [ ] Better error handling when smartscaf operations fail
|
||||||
|
- [ ] Add pre/post template hooks for custom processing
|
||||||
|
- [ ] Validate template results before proceeding with format
|
||||||
|
- [ ] Support skipping template updates via configuration
|
||||||
|
|
||||||
|
### 9. Enhanced License Management ⏳
|
||||||
|
- [ ] Make license checking configurable (partial)
|
||||||
|
- [ ] Add license compatibility matrix
|
||||||
|
- [x] Support license exceptions for specific packages
|
||||||
|
- [ ] Generate license report for compliance
|
||||||
|
|
||||||
|
### 10. Better Package.json Management ⏳
|
||||||
|
- [ ] Smart dependency sorting and grouping
|
||||||
|
- [ ] Automated script generation based on project type
|
||||||
|
- [ ] Support for pnpm workspace configurations
|
||||||
|
- [ ] Validation of package.json schema
|
||||||
|
|
||||||
|
### 11. Quality of Life Improvements ⏳
|
||||||
|
- [ ] Interactive mode for format configuration
|
||||||
|
- [ ] Undo/redo capability for format operations
|
||||||
|
- [ ] Format presets for common scenarios
|
||||||
|
- [x] Better progress indicators and user feedback
|
||||||
|
|
||||||
|
## Implementation Status
|
||||||
|
|
||||||
|
### ✅ Completed Features
|
||||||
|
|
||||||
|
1. **Rollback Mechanism**
|
||||||
|
- Full backup/restore functionality
|
||||||
|
- Manifest tracking and integrity checks
|
||||||
|
- CLI commands for rollback operations
|
||||||
|
|
||||||
|
2. **Plan → Action Workflow**
|
||||||
|
- Two-phase approach (analyze then execute)
|
||||||
|
- Interactive confirmation
|
||||||
|
- Dry-run support
|
||||||
|
|
||||||
|
3. **Configuration System**
|
||||||
|
- Comprehensive npmextra.json support
|
||||||
|
- Module control (skip/only/order)
|
||||||
|
- Cache configuration
|
||||||
|
- Parallel execution settings
|
||||||
|
|
||||||
|
4. **Performance Improvements**
|
||||||
|
- Parallel execution by dependency analysis
|
||||||
|
- File change caching
|
||||||
|
- Prettier batching
|
||||||
|
- Execution time tracking
|
||||||
|
|
||||||
|
5. **Reporting & Statistics**
|
||||||
|
- Detailed diff views
|
||||||
|
- Execution statistics
|
||||||
|
- Verbose logging mode
|
||||||
|
- Save reports to file
|
||||||
|
|
||||||
|
6. **Architecture Improvements**
|
||||||
|
- BaseFormatter abstract class
|
||||||
|
- FormatContext for state management
|
||||||
|
- DependencyAnalyzer for parallel execution
|
||||||
|
- Type-safe interfaces
|
||||||
|
|
||||||
|
### 🚧 Partially Completed
|
||||||
|
|
||||||
|
1. **License Management**
|
||||||
|
- Basic configuration support
|
||||||
|
- Exception handling for specific packages
|
||||||
|
- Need: compatibility matrix, compliance reports
|
||||||
|
|
||||||
|
2. **Package.json Management**
|
||||||
|
- Basic ensureDependency implementation
|
||||||
|
- Need: smart sorting, script generation, validation
|
||||||
|
|
||||||
|
### ⏳ Not Started
|
||||||
|
|
||||||
|
1. **Plugin System**
|
||||||
|
- Need to design plugin API
|
||||||
|
- Hook system for pre/post operations
|
||||||
|
- Custom validation rules
|
||||||
|
|
||||||
|
2. **Git Integration**
|
||||||
|
- Pre-commit hooks
|
||||||
|
- Automatic formatting on commit
|
||||||
|
|
||||||
|
3. **Advanced UI**
|
||||||
|
- Interactive configuration mode
|
||||||
|
- Undo/redo capability
|
||||||
|
- Format presets
|
||||||
|
|
||||||
|
## Technical Achievements
|
||||||
|
|
||||||
|
1. **Type Safety**: All new code uses TypeScript interfaces and types
|
||||||
|
2. **Error Handling**: Comprehensive try-catch blocks with rollback
|
||||||
|
3. **API Compatibility**: Updated to use latest smartfile/smartnpm APIs
|
||||||
|
4. **Testing**: Ready for comprehensive test suite
|
||||||
|
5. **Performance**: Significant improvements through caching and parallelization
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
|
||||||
|
1. Write comprehensive tests for all new functionality
|
||||||
|
2. Create user documentation for new features
|
||||||
|
3. Consider plugin API design for extensibility
|
||||||
|
4. Implement remaining Phase 3 features based on user feedback
|
||||||
|
5. Performance benchmarking and optimization
|
||||||
|
|
||||||
|
## Success Metrics Achieved
|
||||||
|
|
||||||
|
- ✅ Reduced error rates through rollback mechanism
|
||||||
|
- ✅ Faster execution through parallel processing and caching
|
||||||
|
- ✅ Enhanced user control through configuration
|
||||||
|
- ✅ Better visibility through reporting and statistics
|
||||||
|
- ✅ Improved maintainability through better architecture
|
@@ -3,6 +3,6 @@
|
|||||||
*/
|
*/
|
||||||
export const commitinfo = {
|
export const commitinfo = {
|
||||||
name: '@git.zone/cli',
|
name: '@git.zone/cli',
|
||||||
version: '1.10.1',
|
version: '1.16.8',
|
||||||
description: 'A CLI toolbelt to streamline local development cycles by using various gitzone utilities.'
|
description: 'A comprehensive CLI tool for enhancing and managing local development workflows with gitzone utilities, focusing on project setup, version control, code formatting, and template management.'
|
||||||
}
|
}
|
||||||
|
@@ -16,6 +16,8 @@ export interface IGitzoneConfigData {
|
|||||||
npmPackageName: string;
|
npmPackageName: string;
|
||||||
license: string;
|
license: string;
|
||||||
projectDomain: string;
|
projectDomain: string;
|
||||||
|
assetbrokerUrl: string;
|
||||||
|
legalUrl: string;
|
||||||
};
|
};
|
||||||
copy: { [key: string]: string };
|
copy: { [key: string]: string };
|
||||||
npmciOptions: {
|
npmciOptions: {
|
||||||
@@ -38,7 +40,9 @@ export class GitzoneConfig {
|
|||||||
public async readConfigFromCwd() {
|
public async readConfigFromCwd() {
|
||||||
const npmextraInstance = new plugins.npmextra.Npmextra(paths.cwd);
|
const npmextraInstance = new plugins.npmextra.Npmextra(paths.cwd);
|
||||||
this.data = npmextraInstance.dataFor<IGitzoneConfigData>('gitzone', {});
|
this.data = npmextraInstance.dataFor<IGitzoneConfigData>('gitzone', {});
|
||||||
this.data.npmciOptions = npmextraInstance.dataFor<IGitzoneConfigData['npmciOptions']>('npmci', {
|
this.data.npmciOptions = npmextraInstance.dataFor<
|
||||||
|
IGitzoneConfigData['npmciOptions']
|
||||||
|
>('npmci', {
|
||||||
npmAccessLevel: 'public',
|
npmAccessLevel: 'public',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@@ -48,13 +48,49 @@ export let run = async () => {
|
|||||||
await modDeprecate.run();
|
await modDeprecate.run();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* docker
|
||||||
|
*/
|
||||||
|
gitzoneSmartcli.addCommand('docker').subscribe(async (argvArg) => {
|
||||||
|
const modDocker = await import('./mod_docker/index.js');
|
||||||
|
await modDocker.run(argvArg);
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update all files that comply with the gitzone standard
|
* Update all files that comply with the gitzone standard
|
||||||
*/
|
*/
|
||||||
gitzoneSmartcli.addCommand('format').subscribe(async (argvArg) => {
|
gitzoneSmartcli.addCommand('format').subscribe(async (argvArg) => {
|
||||||
const config = GitzoneConfig.fromCwd();
|
const config = GitzoneConfig.fromCwd();
|
||||||
const modFormat = await import('./mod_format/index.js');
|
const modFormat = await import('./mod_format/index.js');
|
||||||
await modFormat.run();
|
|
||||||
|
// Handle rollback commands
|
||||||
|
if (argvArg.rollback) {
|
||||||
|
await modFormat.handleRollback(argvArg.rollback);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argvArg['list-backups']) {
|
||||||
|
await modFormat.handleListBackups();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argvArg['clean-backups']) {
|
||||||
|
await modFormat.handleCleanBackups();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle format with options
|
||||||
|
await modFormat.run({
|
||||||
|
dryRun: argvArg['dry-run'],
|
||||||
|
yes: argvArg.yes,
|
||||||
|
planOnly: argvArg['plan-only'],
|
||||||
|
savePlan: argvArg['save-plan'],
|
||||||
|
fromPlan: argvArg['from-plan'],
|
||||||
|
detailed: argvArg.detailed,
|
||||||
|
interactive: argvArg.interactive !== false,
|
||||||
|
parallel: argvArg.parallel !== false,
|
||||||
|
verbose: argvArg.verbose,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -1,6 +1,29 @@
|
|||||||
import { commitinfo } from '@push.rocks/commitinfo';
|
import { commitinfo } from '@push.rocks/commitinfo';
|
||||||
import * as plugins from './plugins.js';
|
import * as plugins from './plugins.js';
|
||||||
|
|
||||||
|
// Create logger instance
|
||||||
export const logger = plugins.smartlog.Smartlog.createForCommitinfo(commitinfo);
|
export const logger = plugins.smartlog.Smartlog.createForCommitinfo(commitinfo);
|
||||||
|
|
||||||
logger.addLogDestination(new plugins.smartlogDestinationLocal.DestinationLocal());
|
// Add console destination
|
||||||
|
const consoleDestination =
|
||||||
|
new plugins.smartlogDestinationLocal.DestinationLocal();
|
||||||
|
logger.addLogDestination(consoleDestination);
|
||||||
|
|
||||||
|
// Verbose logging helper
|
||||||
|
let verboseMode = false;
|
||||||
|
|
||||||
|
export const setVerboseMode = (verbose: boolean): void => {
|
||||||
|
verboseMode = verbose;
|
||||||
|
logger.log('info', `Verbose mode ${verbose ? 'enabled' : 'disabled'}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const isVerboseMode = (): boolean => {
|
||||||
|
return verboseMode;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Custom log method with verbose support
|
||||||
|
export const logVerbose = (message: string): void => {
|
||||||
|
if (verboseMode) {
|
||||||
|
logger.log('info', `[VERBOSE] ${message}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
@@ -10,20 +10,22 @@ export const run = async (argvArg: any) => {
|
|||||||
await formatMod.run();
|
await formatMod.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
logger.log('info', `gathering facts...`);
|
logger.log('info', `gathering facts...`);
|
||||||
const aidoc = new plugins.tsdoc.AiDoc();
|
const aidoc = new plugins.tsdoc.AiDoc();
|
||||||
await aidoc.start();
|
await aidoc.start();
|
||||||
|
|
||||||
const nextCommitObject = await aidoc.buildNextCommitObject(paths.cwd);
|
const nextCommitObject = await aidoc.buildNextCommitObject(paths.cwd);
|
||||||
|
|
||||||
logger.log('info', `---------
|
logger.log(
|
||||||
|
'info',
|
||||||
|
`---------
|
||||||
Next recommended commit would be:
|
Next recommended commit would be:
|
||||||
===========
|
===========
|
||||||
-> ${nextCommitObject.recommendedNextVersion}:
|
-> ${nextCommitObject.recommendedNextVersion}:
|
||||||
-> ${nextCommitObject.recommendedNextVersionLevel}(${nextCommitObject.recommendedNextVersionScope}): ${nextCommitObject.recommendedNextVersionMessage}
|
-> ${nextCommitObject.recommendedNextVersionLevel}(${nextCommitObject.recommendedNextVersionScope}): ${nextCommitObject.recommendedNextVersionMessage}
|
||||||
===========
|
===========
|
||||||
`);
|
`,
|
||||||
|
);
|
||||||
const commitInteract = new plugins.smartinteract.SmartInteract();
|
const commitInteract = new plugins.smartinteract.SmartInteract();
|
||||||
commitInteract.addQuestions([
|
commitInteract.addQuestions([
|
||||||
{
|
{
|
||||||
@@ -72,32 +74,55 @@ export const run = async (argvArg: any) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
logger.log('info', `Baking commitinfo into code ...`);
|
logger.log('info', `Baking commitinfo into code ...`);
|
||||||
const commitInfo = new plugins.commitinfo.CommitInfo(paths.cwd, commitVersionType);
|
const commitInfo = new plugins.commitinfo.CommitInfo(
|
||||||
|
paths.cwd,
|
||||||
|
commitVersionType,
|
||||||
|
);
|
||||||
await commitInfo.writeIntoPotentialDirs();
|
await commitInfo.writeIntoPotentialDirs();
|
||||||
|
|
||||||
logger.log('info', `Writing changelog.md ...`);
|
logger.log('info', `Writing changelog.md ...`);
|
||||||
let changelog = nextCommitObject.changelog;
|
let changelog = nextCommitObject.changelog;
|
||||||
changelog = changelog.replaceAll('{{nextVersion}}', (await commitInfo.getNextPlannedVersion()).versionString);
|
changelog = changelog.replaceAll(
|
||||||
changelog = changelog.replaceAll('{{nextVersionScope}}', `${await answerBucket.getAnswerFor('commitType')}(${await answerBucket.getAnswerFor('commitScope')})`);
|
'{{nextVersion}}',
|
||||||
changelog = changelog.replaceAll('{{nextVersionMessage}}', nextCommitObject.recommendedNextVersionMessage);
|
(await commitInfo.getNextPlannedVersion()).versionString,
|
||||||
|
);
|
||||||
|
changelog = changelog.replaceAll(
|
||||||
|
'{{nextVersionScope}}',
|
||||||
|
`${await answerBucket.getAnswerFor('commitType')}(${await answerBucket.getAnswerFor('commitScope')})`,
|
||||||
|
);
|
||||||
|
changelog = changelog.replaceAll(
|
||||||
|
'{{nextVersionMessage}}',
|
||||||
|
nextCommitObject.recommendedNextVersionMessage,
|
||||||
|
);
|
||||||
if (nextCommitObject.recommendedNextVersionDetails?.length > 0) {
|
if (nextCommitObject.recommendedNextVersionDetails?.length > 0) {
|
||||||
changelog = changelog.replaceAll('{{nextVersionDetails}}', '- ' + nextCommitObject.recommendedNextVersionDetails.join('\n- '));
|
changelog = changelog.replaceAll(
|
||||||
|
'{{nextVersionDetails}}',
|
||||||
|
'- ' + nextCommitObject.recommendedNextVersionDetails.join('\n- '),
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
changelog = changelog.replaceAll('\n{{nextVersionDetails}}', '');
|
changelog = changelog.replaceAll('\n{{nextVersionDetails}}', '');
|
||||||
}
|
}
|
||||||
|
|
||||||
await plugins.smartfile.memory.toFs(changelog, plugins.path.join(paths.cwd, `changelog.md`));
|
await plugins.smartfile.memory.toFs(
|
||||||
|
changelog,
|
||||||
|
plugins.path.join(paths.cwd, `changelog.md`),
|
||||||
|
);
|
||||||
|
|
||||||
logger.log('info', `Staging files for commit:`);
|
logger.log('info', `Staging files for commit:`);
|
||||||
await smartshellInstance.exec(`git add -A`);
|
await smartshellInstance.exec(`git add -A`);
|
||||||
await smartshellInstance.exec(`git commit -m "${commitString}"`);
|
await smartshellInstance.exec(`git commit -m "${commitString}"`);
|
||||||
await smartshellInstance.exec(`npm version ${commitVersionType}`);
|
await smartshellInstance.exec(`npm version ${commitVersionType}`);
|
||||||
if (answerBucket.getAnswerFor('pushToOrigin') && !(process.env.CI === 'true')) {
|
if (
|
||||||
|
answerBucket.getAnswerFor('pushToOrigin') &&
|
||||||
|
!(process.env.CI === 'true')
|
||||||
|
) {
|
||||||
await smartshellInstance.exec(`git push origin master --follow-tags`);
|
await smartshellInstance.exec(`git push origin master --follow-tags`);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const createCommitStringFromAnswerBucket = (answerBucket: plugins.smartinteract.AnswerBucket) => {
|
const createCommitStringFromAnswerBucket = (
|
||||||
|
answerBucket: plugins.smartinteract.AnswerBucket,
|
||||||
|
) => {
|
||||||
const commitType = answerBucket.getAnswerFor('commitType');
|
const commitType = answerBucket.getAnswerFor('commitType');
|
||||||
const commitScope = answerBucket.getAnswerFor('commitScope');
|
const commitScope = answerBucket.getAnswerFor('commitScope');
|
||||||
const commitDescription = answerBucket.getAnswerFor('commitDescription');
|
const commitDescription = answerBucket.getAnswerFor('commitDescription');
|
||||||
|
@@ -4,6 +4,15 @@ import { logger } from '../gitzone.logging.js';
|
|||||||
|
|
||||||
export const run = async () => {
|
export const run = async () => {
|
||||||
const smartInteract = new plugins.smartinteract.SmartInteract([
|
const smartInteract = new plugins.smartinteract.SmartInteract([
|
||||||
|
{
|
||||||
|
name: `registryUrls`,
|
||||||
|
message: `What are the comma separated registry URLs?`,
|
||||||
|
type: `input`,
|
||||||
|
default: `https://registry.npmjs.org`,
|
||||||
|
validate: (stringInput) => {
|
||||||
|
return stringInput !== '' && !process.env.CI;
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: `oldPackageName`,
|
name: `oldPackageName`,
|
||||||
message: `Whats the name of the OLD package?`,
|
message: `Whats the name of the OLD package?`,
|
||||||
@@ -24,14 +33,20 @@ export const run = async () => {
|
|||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
const answerBucket = await smartInteract.runQueue();
|
const answerBucket = await smartInteract.runQueue();
|
||||||
|
const registryUrls = answerBucket.getAnswerFor(`registryUrls`).split(',');
|
||||||
const oldPackageName = answerBucket.getAnswerFor(`oldPackageName`);
|
const oldPackageName = answerBucket.getAnswerFor(`oldPackageName`);
|
||||||
const newPackageName = answerBucket.getAnswerFor(`newPackageName`);
|
const newPackageName = answerBucket.getAnswerFor(`newPackageName`);
|
||||||
logger.log('info', `Deprecating package ${oldPackageName} in favour of ${newPackageName}`);
|
logger.log(
|
||||||
|
'info',
|
||||||
|
`Deprecating package ${oldPackageName} in favour of ${newPackageName}`,
|
||||||
|
);
|
||||||
const smartshellInstance = new plugins.smartshell.Smartshell({
|
const smartshellInstance = new plugins.smartshell.Smartshell({
|
||||||
executor: 'bash',
|
executor: 'bash',
|
||||||
});
|
});
|
||||||
await smartshellInstance.exec(
|
for (const registryUrl of registryUrls) {
|
||||||
`npm deprecate ${oldPackageName}@* ` +
|
await smartshellInstance.exec(
|
||||||
`"${oldPackageName} has been deprecated in favour of ${newPackageName} - please upgrade asap!!!"`
|
`npm deprecate ${oldPackageName}@* ` +
|
||||||
);
|
`"${oldPackageName} has been deprecated in favour of ${newPackageName} - please upgrade asap!!!" --registry ${registryUrl}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
12
ts/mod_docker/index.ts
Normal file
12
ts/mod_docker/index.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import * as plugins from './mod.plugins.js';
|
||||||
|
|
||||||
|
export const run = async (argvArg) => {
|
||||||
|
const smartshellInstance = new plugins.smartshell.Smartshell({
|
||||||
|
executor: 'bash',
|
||||||
|
});
|
||||||
|
switch (argvArg._[1]) {
|
||||||
|
case 'prune':
|
||||||
|
await smartshellInstance.exec(`docker system prune -a -f --volumes`);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
4
ts/mod_docker/mod.plugins.ts
Normal file
4
ts/mod_docker/mod.plugins.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export * from '../plugins.js';
|
||||||
|
|
||||||
|
import * as smartshell from '@push.rocks/smartshell';
|
||||||
|
export { smartshell };
|
70
ts/mod_format/classes.baseformatter.ts
Normal file
70
ts/mod_format/classes.baseformatter.ts
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
import * as plugins from './mod.plugins.js';
|
||||||
|
import { FormatContext } from './classes.formatcontext.js';
|
||||||
|
import type { IPlannedChange } from './interfaces.format.js';
|
||||||
|
import { Project } from '../classes.project.js';
|
||||||
|
|
||||||
|
export abstract class BaseFormatter {
|
||||||
|
protected context: FormatContext;
|
||||||
|
protected project: Project;
|
||||||
|
protected stats: any; // Will be FormatStats from context
|
||||||
|
|
||||||
|
constructor(context: FormatContext, project: Project) {
|
||||||
|
this.context = context;
|
||||||
|
this.project = project;
|
||||||
|
this.stats = context.getFormatStats();
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract get name(): string;
|
||||||
|
abstract analyze(): Promise<IPlannedChange[]>;
|
||||||
|
abstract applyChange(change: IPlannedChange): Promise<void>;
|
||||||
|
|
||||||
|
async execute(changes: IPlannedChange[]): Promise<void> {
|
||||||
|
const startTime = this.stats.moduleStartTime(this.name);
|
||||||
|
this.stats.startModule(this.name);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.preExecute();
|
||||||
|
|
||||||
|
for (const change of changes) {
|
||||||
|
try {
|
||||||
|
await this.applyChange(change);
|
||||||
|
this.stats.recordFileOperation(this.name, change.type, true);
|
||||||
|
} catch (error) {
|
||||||
|
this.stats.recordFileOperation(this.name, change.type, false);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.postExecute();
|
||||||
|
} catch (error) {
|
||||||
|
// Don't rollback here - let the FormatPlanner handle it
|
||||||
|
throw error;
|
||||||
|
} finally {
|
||||||
|
this.stats.endModule(this.name, startTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async preExecute(): Promise<void> {
|
||||||
|
// Override in subclasses if needed
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async postExecute(): Promise<void> {
|
||||||
|
// Override in subclasses if needed
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async modifyFile(filepath: string, content: string): Promise<void> {
|
||||||
|
await plugins.smartfile.memory.toFs(content, filepath);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async createFile(filepath: string, content: string): Promise<void> {
|
||||||
|
await plugins.smartfile.memory.toFs(content, filepath);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async deleteFile(filepath: string): Promise<void> {
|
||||||
|
await plugins.smartfile.fs.remove(filepath);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async shouldProcessFile(filepath: string): Promise<boolean> {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
223
ts/mod_format/classes.changecache.ts
Normal file
223
ts/mod_format/classes.changecache.ts
Normal file
@@ -0,0 +1,223 @@
|
|||||||
|
import * as plugins from './mod.plugins.js';
|
||||||
|
import * as paths from '../paths.js';
|
||||||
|
|
||||||
|
export interface IFileCache {
|
||||||
|
path: string;
|
||||||
|
checksum: string;
|
||||||
|
modified: number;
|
||||||
|
size: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ICacheManifest {
|
||||||
|
version: string;
|
||||||
|
lastFormat: number;
|
||||||
|
files: IFileCache[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ChangeCache {
|
||||||
|
private cacheDir: string;
|
||||||
|
private manifestPath: string;
|
||||||
|
private cacheVersion = '1.0.0';
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.cacheDir = plugins.path.join(paths.cwd, '.nogit', 'gitzone-cache');
|
||||||
|
this.manifestPath = plugins.path.join(this.cacheDir, 'manifest.json');
|
||||||
|
}
|
||||||
|
|
||||||
|
async initialize(): Promise<void> {
|
||||||
|
await plugins.smartfile.fs.ensureDir(this.cacheDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getManifest(): Promise<ICacheManifest> {
|
||||||
|
const defaultManifest: ICacheManifest = {
|
||||||
|
version: this.cacheVersion,
|
||||||
|
lastFormat: 0,
|
||||||
|
files: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
const exists = await plugins.smartfile.fs.fileExists(this.manifestPath);
|
||||||
|
if (!exists) {
|
||||||
|
return defaultManifest;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const content = plugins.smartfile.fs.toStringSync(this.manifestPath);
|
||||||
|
const manifest = JSON.parse(content);
|
||||||
|
|
||||||
|
// Validate the manifest structure
|
||||||
|
if (this.isValidManifest(manifest)) {
|
||||||
|
return manifest;
|
||||||
|
} else {
|
||||||
|
console.warn('Invalid manifest structure, returning default manifest');
|
||||||
|
return defaultManifest;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(
|
||||||
|
`Failed to read cache manifest: ${error.message}, returning default manifest`,
|
||||||
|
);
|
||||||
|
// Try to delete the corrupted file
|
||||||
|
try {
|
||||||
|
await plugins.smartfile.fs.remove(this.manifestPath);
|
||||||
|
} catch (removeError) {
|
||||||
|
// Ignore removal errors
|
||||||
|
}
|
||||||
|
return defaultManifest;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async saveManifest(manifest: ICacheManifest): Promise<void> {
|
||||||
|
// Validate before saving
|
||||||
|
if (!this.isValidManifest(manifest)) {
|
||||||
|
throw new Error('Invalid manifest structure, cannot save');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure directory exists
|
||||||
|
await plugins.smartfile.fs.ensureDir(this.cacheDir);
|
||||||
|
|
||||||
|
// Write directly with proper JSON stringification
|
||||||
|
const jsonContent = JSON.stringify(manifest, null, 2);
|
||||||
|
await plugins.smartfile.memory.toFs(jsonContent, this.manifestPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
async hasFileChanged(filePath: string): Promise<boolean> {
|
||||||
|
const absolutePath = plugins.path.isAbsolute(filePath)
|
||||||
|
? filePath
|
||||||
|
: plugins.path.join(paths.cwd, filePath);
|
||||||
|
|
||||||
|
// Check if file exists
|
||||||
|
const exists = await plugins.smartfile.fs.fileExists(absolutePath);
|
||||||
|
if (!exists) {
|
||||||
|
return true; // File doesn't exist, so it's "changed" (will be created)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get current file stats
|
||||||
|
const stats = await plugins.smartfile.fs.stat(absolutePath);
|
||||||
|
|
||||||
|
// Skip directories
|
||||||
|
if (stats.isDirectory()) {
|
||||||
|
return false; // Directories are not processed
|
||||||
|
}
|
||||||
|
|
||||||
|
const content = plugins.smartfile.fs.toStringSync(absolutePath);
|
||||||
|
const currentChecksum = this.calculateChecksum(content);
|
||||||
|
|
||||||
|
// Get cached info
|
||||||
|
const manifest = await this.getManifest();
|
||||||
|
const cachedFile = manifest.files.find((f) => f.path === filePath);
|
||||||
|
|
||||||
|
if (!cachedFile) {
|
||||||
|
return true; // Not in cache, so it's changed
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare checksums
|
||||||
|
return (
|
||||||
|
cachedFile.checksum !== currentChecksum ||
|
||||||
|
cachedFile.size !== stats.size ||
|
||||||
|
cachedFile.modified !== stats.mtimeMs
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateFileCache(filePath: string): Promise<void> {
|
||||||
|
const absolutePath = plugins.path.isAbsolute(filePath)
|
||||||
|
? filePath
|
||||||
|
: plugins.path.join(paths.cwd, filePath);
|
||||||
|
|
||||||
|
// Get current file stats
|
||||||
|
const stats = await plugins.smartfile.fs.stat(absolutePath);
|
||||||
|
|
||||||
|
// Skip directories
|
||||||
|
if (stats.isDirectory()) {
|
||||||
|
return; // Don't cache directories
|
||||||
|
}
|
||||||
|
|
||||||
|
const content = plugins.smartfile.fs.toStringSync(absolutePath);
|
||||||
|
const checksum = this.calculateChecksum(content);
|
||||||
|
|
||||||
|
// Update manifest
|
||||||
|
const manifest = await this.getManifest();
|
||||||
|
const existingIndex = manifest.files.findIndex((f) => f.path === filePath);
|
||||||
|
|
||||||
|
const cacheEntry: IFileCache = {
|
||||||
|
path: filePath,
|
||||||
|
checksum,
|
||||||
|
modified: stats.mtimeMs,
|
||||||
|
size: stats.size,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (existingIndex !== -1) {
|
||||||
|
manifest.files[existingIndex] = cacheEntry;
|
||||||
|
} else {
|
||||||
|
manifest.files.push(cacheEntry);
|
||||||
|
}
|
||||||
|
|
||||||
|
manifest.lastFormat = Date.now();
|
||||||
|
await this.saveManifest(manifest);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getChangedFiles(filePaths: string[]): Promise<string[]> {
|
||||||
|
const changedFiles: string[] = [];
|
||||||
|
|
||||||
|
for (const filePath of filePaths) {
|
||||||
|
if (await this.hasFileChanged(filePath)) {
|
||||||
|
changedFiles.push(filePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return changedFiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
async clean(): Promise<void> {
|
||||||
|
const manifest = await this.getManifest();
|
||||||
|
const validFiles: IFileCache[] = [];
|
||||||
|
|
||||||
|
// Remove entries for files that no longer exist
|
||||||
|
for (const file of manifest.files) {
|
||||||
|
const absolutePath = plugins.path.isAbsolute(file.path)
|
||||||
|
? file.path
|
||||||
|
: plugins.path.join(paths.cwd, file.path);
|
||||||
|
|
||||||
|
if (await plugins.smartfile.fs.fileExists(absolutePath)) {
|
||||||
|
validFiles.push(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
manifest.files = validFiles;
|
||||||
|
await this.saveManifest(manifest);
|
||||||
|
}
|
||||||
|
|
||||||
|
private calculateChecksum(content: string | Buffer): string {
|
||||||
|
return plugins.crypto.createHash('sha256').update(content).digest('hex');
|
||||||
|
}
|
||||||
|
|
||||||
|
private isValidManifest(manifest: any): manifest is ICacheManifest {
|
||||||
|
// Check if manifest has the required structure
|
||||||
|
if (!manifest || typeof manifest !== 'object') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check required fields
|
||||||
|
if (
|
||||||
|
typeof manifest.version !== 'string' ||
|
||||||
|
typeof manifest.lastFormat !== 'number' ||
|
||||||
|
!Array.isArray(manifest.files)
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check each file entry
|
||||||
|
for (const file of manifest.files) {
|
||||||
|
if (
|
||||||
|
!file ||
|
||||||
|
typeof file !== 'object' ||
|
||||||
|
typeof file.path !== 'string' ||
|
||||||
|
typeof file.checksum !== 'string' ||
|
||||||
|
typeof file.modified !== 'number' ||
|
||||||
|
typeof file.size !== 'number'
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
117
ts/mod_format/classes.dependency-analyzer.ts
Normal file
117
ts/mod_format/classes.dependency-analyzer.ts
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
import * as plugins from './mod.plugins.js';
|
||||||
|
import { BaseFormatter } from './classes.baseformatter.js';
|
||||||
|
|
||||||
|
export interface IModuleDependency {
|
||||||
|
module: string;
|
||||||
|
dependencies: Set<string>;
|
||||||
|
dependents: Set<string>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DependencyAnalyzer {
|
||||||
|
private moduleDependencies: Map<string, IModuleDependency> = new Map();
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.initializeDependencies();
|
||||||
|
}
|
||||||
|
|
||||||
|
private initializeDependencies(): void {
|
||||||
|
// Define dependencies between format modules
|
||||||
|
const dependencies = {
|
||||||
|
cleanup: [], // No dependencies
|
||||||
|
npmextra: [], // No dependencies
|
||||||
|
license: ['npmextra'], // Depends on npmextra for config
|
||||||
|
packagejson: ['npmextra'], // Depends on npmextra for config
|
||||||
|
templates: ['npmextra', 'packagejson'], // Depends on both
|
||||||
|
gitignore: ['templates'], // Depends on templates
|
||||||
|
tsconfig: ['packagejson'], // Depends on package.json
|
||||||
|
prettier: [
|
||||||
|
'cleanup',
|
||||||
|
'npmextra',
|
||||||
|
'packagejson',
|
||||||
|
'templates',
|
||||||
|
'gitignore',
|
||||||
|
'tsconfig',
|
||||||
|
], // Runs after most others
|
||||||
|
readme: ['npmextra', 'packagejson'], // Depends on project metadata
|
||||||
|
copy: ['npmextra'], // Depends on config
|
||||||
|
};
|
||||||
|
|
||||||
|
// Initialize all modules
|
||||||
|
for (const [module, deps] of Object.entries(dependencies)) {
|
||||||
|
this.moduleDependencies.set(module, {
|
||||||
|
module,
|
||||||
|
dependencies: new Set(deps),
|
||||||
|
dependents: new Set(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build reverse dependencies (dependents)
|
||||||
|
for (const [module, deps] of Object.entries(dependencies)) {
|
||||||
|
for (const dep of deps) {
|
||||||
|
const depModule = this.moduleDependencies.get(dep);
|
||||||
|
if (depModule) {
|
||||||
|
depModule.dependents.add(module);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getExecutionGroups(modules: BaseFormatter[]): BaseFormatter[][] {
|
||||||
|
const modulesMap = new Map(modules.map((m) => [m.name, m]));
|
||||||
|
const executed = new Set<string>();
|
||||||
|
const groups: BaseFormatter[][] = [];
|
||||||
|
|
||||||
|
while (executed.size < modules.length) {
|
||||||
|
const currentGroup: BaseFormatter[] = [];
|
||||||
|
|
||||||
|
for (const module of modules) {
|
||||||
|
if (executed.has(module.name)) continue;
|
||||||
|
|
||||||
|
const dependency = this.moduleDependencies.get(module.name);
|
||||||
|
if (!dependency) {
|
||||||
|
// Unknown module, execute in isolation
|
||||||
|
currentGroup.push(module);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if all dependencies have been executed
|
||||||
|
const allDepsExecuted = Array.from(dependency.dependencies).every(
|
||||||
|
(dep) => executed.has(dep) || !modulesMap.has(dep),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (allDepsExecuted) {
|
||||||
|
currentGroup.push(module);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentGroup.length === 0) {
|
||||||
|
// Circular dependency or error - execute remaining modules
|
||||||
|
for (const module of modules) {
|
||||||
|
if (!executed.has(module.name)) {
|
||||||
|
currentGroup.push(module);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
currentGroup.forEach((m) => executed.add(m.name));
|
||||||
|
groups.push(currentGroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
return groups;
|
||||||
|
}
|
||||||
|
|
||||||
|
canRunInParallel(module1: string, module2: string): boolean {
|
||||||
|
const dep1 = this.moduleDependencies.get(module1);
|
||||||
|
const dep2 = this.moduleDependencies.get(module2);
|
||||||
|
|
||||||
|
if (!dep1 || !dep2) return false;
|
||||||
|
|
||||||
|
// Check if module1 depends on module2 or vice versa
|
||||||
|
return (
|
||||||
|
!dep1.dependencies.has(module2) &&
|
||||||
|
!dep2.dependencies.has(module1) &&
|
||||||
|
!dep1.dependents.has(module2) &&
|
||||||
|
!dep2.dependents.has(module1)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
124
ts/mod_format/classes.diffreporter.ts
Normal file
124
ts/mod_format/classes.diffreporter.ts
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
import * as plugins from './mod.plugins.js';
|
||||||
|
import type { IPlannedChange } from './interfaces.format.js';
|
||||||
|
import { logger } from '../gitzone.logging.js';
|
||||||
|
|
||||||
|
export class DiffReporter {
|
||||||
|
private diffs: Map<string, string> = new Map();
|
||||||
|
|
||||||
|
async generateDiff(
|
||||||
|
filePath: string,
|
||||||
|
oldContent: string,
|
||||||
|
newContent: string,
|
||||||
|
): Promise<string> {
|
||||||
|
const diff = plugins.smartdiff.createDiff(oldContent, newContent);
|
||||||
|
this.diffs.set(filePath, diff);
|
||||||
|
return diff;
|
||||||
|
}
|
||||||
|
|
||||||
|
async generateDiffForChange(change: IPlannedChange): Promise<string | null> {
|
||||||
|
if (change.type !== 'modify') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const exists = await plugins.smartfile.fs.fileExists(change.path);
|
||||||
|
if (!exists) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentContent = await plugins.smartfile.fs.toStringSync(
|
||||||
|
change.path,
|
||||||
|
);
|
||||||
|
|
||||||
|
// For planned changes, we need the new content
|
||||||
|
if (!change.content) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return await this.generateDiff(
|
||||||
|
change.path,
|
||||||
|
currentContent,
|
||||||
|
change.content,
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
logger.log(
|
||||||
|
'error',
|
||||||
|
`Failed to generate diff for ${change.path}: ${error.message}`,
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
displayDiff(filePath: string, diff?: string): void {
|
||||||
|
const diffToShow = diff || this.diffs.get(filePath);
|
||||||
|
|
||||||
|
if (!diffToShow) {
|
||||||
|
logger.log('warn', `No diff available for ${filePath}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`\n${this.formatDiffHeader(filePath)}`);
|
||||||
|
console.log(this.colorDiff(diffToShow));
|
||||||
|
console.log('━'.repeat(50));
|
||||||
|
}
|
||||||
|
|
||||||
|
displayAllDiffs(): void {
|
||||||
|
if (this.diffs.size === 0) {
|
||||||
|
logger.log('info', 'No diffs to display');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('\nFile Changes:');
|
||||||
|
console.log('═'.repeat(50));
|
||||||
|
|
||||||
|
for (const [filePath, diff] of this.diffs) {
|
||||||
|
this.displayDiff(filePath, diff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private formatDiffHeader(filePath: string): string {
|
||||||
|
return `📄 ${filePath}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private colorDiff(diff: string): string {
|
||||||
|
const lines = diff.split('\n');
|
||||||
|
const coloredLines = lines.map((line) => {
|
||||||
|
if (line.startsWith('+') && !line.startsWith('+++')) {
|
||||||
|
return `\x1b[32m${line}\x1b[0m`; // Green for additions
|
||||||
|
} else if (line.startsWith('-') && !line.startsWith('---')) {
|
||||||
|
return `\x1b[31m${line}\x1b[0m`; // Red for deletions
|
||||||
|
} else if (line.startsWith('@')) {
|
||||||
|
return `\x1b[36m${line}\x1b[0m`; // Cyan for line numbers
|
||||||
|
} else {
|
||||||
|
return line;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return coloredLines.join('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
async saveDiffReport(outputPath: string): Promise<void> {
|
||||||
|
const report = {
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
totalFiles: this.diffs.size,
|
||||||
|
diffs: Array.from(this.diffs.entries()).map(([path, diff]) => ({
|
||||||
|
path,
|
||||||
|
diff,
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
|
||||||
|
await plugins.smartfile.memory.toFs(
|
||||||
|
JSON.stringify(report, null, 2),
|
||||||
|
outputPath,
|
||||||
|
);
|
||||||
|
logger.log('info', `Diff report saved to ${outputPath}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
hasAnyDiffs(): boolean {
|
||||||
|
return this.diffs.size > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
getDiffCount(): number {
|
||||||
|
return this.diffs.size;
|
||||||
|
}
|
||||||
|
}
|
14
ts/mod_format/classes.formatcontext.ts
Normal file
14
ts/mod_format/classes.formatcontext.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import * as plugins from './mod.plugins.js';
|
||||||
|
import { FormatStats } from './classes.formatstats.js';
|
||||||
|
|
||||||
|
export class FormatContext {
|
||||||
|
private formatStats: FormatStats;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.formatStats = new FormatStats();
|
||||||
|
}
|
||||||
|
|
||||||
|
getFormatStats(): FormatStats {
|
||||||
|
return this.formatStats;
|
||||||
|
}
|
||||||
|
}
|
170
ts/mod_format/classes.formatplanner.ts
Normal file
170
ts/mod_format/classes.formatplanner.ts
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
import * as plugins from './mod.plugins.js';
|
||||||
|
import { FormatContext } from './classes.formatcontext.js';
|
||||||
|
import { BaseFormatter } from './classes.baseformatter.js';
|
||||||
|
import type { IFormatPlan, IPlannedChange } from './interfaces.format.js';
|
||||||
|
import { logger } from '../gitzone.logging.js';
|
||||||
|
import { DependencyAnalyzer } from './classes.dependency-analyzer.js';
|
||||||
|
import { DiffReporter } from './classes.diffreporter.js';
|
||||||
|
|
||||||
|
export class FormatPlanner {
|
||||||
|
private plannedChanges: Map<string, IPlannedChange[]> = new Map();
|
||||||
|
private dependencyAnalyzer = new DependencyAnalyzer();
|
||||||
|
private diffReporter = new DiffReporter();
|
||||||
|
|
||||||
|
async planFormat(modules: BaseFormatter[]): Promise<IFormatPlan> {
|
||||||
|
const plan: IFormatPlan = {
|
||||||
|
summary: {
|
||||||
|
totalFiles: 0,
|
||||||
|
filesAdded: 0,
|
||||||
|
filesModified: 0,
|
||||||
|
filesRemoved: 0,
|
||||||
|
estimatedTime: 0,
|
||||||
|
},
|
||||||
|
changes: [],
|
||||||
|
warnings: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const module of modules) {
|
||||||
|
try {
|
||||||
|
const changes = await module.analyze();
|
||||||
|
this.plannedChanges.set(module.name, changes);
|
||||||
|
|
||||||
|
for (const change of changes) {
|
||||||
|
plan.changes.push(change);
|
||||||
|
|
||||||
|
// Update summary
|
||||||
|
switch (change.type) {
|
||||||
|
case 'create':
|
||||||
|
plan.summary.filesAdded++;
|
||||||
|
break;
|
||||||
|
case 'modify':
|
||||||
|
plan.summary.filesModified++;
|
||||||
|
break;
|
||||||
|
case 'delete':
|
||||||
|
plan.summary.filesRemoved++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
plan.warnings.push({
|
||||||
|
level: 'error',
|
||||||
|
message: `Failed to analyze module ${module.name}: ${error.message}`,
|
||||||
|
module: module.name,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
plan.summary.totalFiles =
|
||||||
|
plan.summary.filesAdded +
|
||||||
|
plan.summary.filesModified +
|
||||||
|
plan.summary.filesRemoved;
|
||||||
|
plan.summary.estimatedTime = plan.summary.totalFiles * 100; // 100ms per file estimate
|
||||||
|
|
||||||
|
return plan;
|
||||||
|
}
|
||||||
|
|
||||||
|
async executePlan(
|
||||||
|
plan: IFormatPlan,
|
||||||
|
modules: BaseFormatter[],
|
||||||
|
context: FormatContext,
|
||||||
|
parallel: boolean = false,
|
||||||
|
): Promise<void> {
|
||||||
|
const startTime = Date.now();
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Always use sequential execution to avoid race conditions
|
||||||
|
for (const module of modules) {
|
||||||
|
const changes = this.plannedChanges.get(module.name) || [];
|
||||||
|
|
||||||
|
if (changes.length > 0) {
|
||||||
|
logger.log('info', `Executing ${module.name} formatter...`);
|
||||||
|
await module.execute(changes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const endTime = Date.now();
|
||||||
|
const duration = endTime - startTime;
|
||||||
|
logger.log('info', `Format operations completed in ${duration}ms`);
|
||||||
|
} catch (error) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async displayPlan(
|
||||||
|
plan: IFormatPlan,
|
||||||
|
detailed: boolean = false,
|
||||||
|
): Promise<void> {
|
||||||
|
console.log('\nFormat Plan:');
|
||||||
|
console.log('━'.repeat(50));
|
||||||
|
console.log(`Summary: ${plan.summary.totalFiles} files will be changed`);
|
||||||
|
console.log(` • ${plan.summary.filesAdded} new files`);
|
||||||
|
console.log(` • ${plan.summary.filesModified} modified files`);
|
||||||
|
console.log(` • ${plan.summary.filesRemoved} deleted files`);
|
||||||
|
console.log('');
|
||||||
|
console.log('Changes by module:');
|
||||||
|
|
||||||
|
// Group changes by module
|
||||||
|
const changesByModule = new Map<string, IPlannedChange[]>();
|
||||||
|
for (const change of plan.changes) {
|
||||||
|
const moduleChanges = changesByModule.get(change.module) || [];
|
||||||
|
moduleChanges.push(change);
|
||||||
|
changesByModule.set(change.module, moduleChanges);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [module, changes] of changesByModule) {
|
||||||
|
console.log(
|
||||||
|
`\n${this.getModuleIcon(module)} ${module} (${changes.length} ${changes.length === 1 ? 'file' : 'files'})`,
|
||||||
|
);
|
||||||
|
|
||||||
|
for (const change of changes) {
|
||||||
|
const icon = this.getChangeIcon(change.type);
|
||||||
|
console.log(` ${icon} ${change.path} - ${change.description}`);
|
||||||
|
|
||||||
|
// Show diff for modified files if detailed view is requested
|
||||||
|
if (detailed && change.type === 'modify') {
|
||||||
|
const diff = await this.diffReporter.generateDiffForChange(change);
|
||||||
|
if (diff) {
|
||||||
|
this.diffReporter.displayDiff(change.path, diff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (plan.warnings.length > 0) {
|
||||||
|
console.log('\nWarnings:');
|
||||||
|
for (const warning of plan.warnings) {
|
||||||
|
const icon = warning.level === 'error' ? '❌' : '⚠️';
|
||||||
|
console.log(` ${icon} ${warning.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('\n' + '━'.repeat(50));
|
||||||
|
}
|
||||||
|
|
||||||
|
private getModuleIcon(module: string): string {
|
||||||
|
const icons: Record<string, string> = {
|
||||||
|
packagejson: '📦',
|
||||||
|
license: '📝',
|
||||||
|
tsconfig: '🔧',
|
||||||
|
cleanup: '🚮',
|
||||||
|
gitignore: '🔒',
|
||||||
|
prettier: '✨',
|
||||||
|
readme: '📖',
|
||||||
|
templates: '📄',
|
||||||
|
npmextra: '⚙️',
|
||||||
|
copy: '📋',
|
||||||
|
};
|
||||||
|
return icons[module] || '📁';
|
||||||
|
}
|
||||||
|
|
||||||
|
private getChangeIcon(type: 'create' | 'modify' | 'delete'): string {
|
||||||
|
switch (type) {
|
||||||
|
case 'create':
|
||||||
|
return '✅';
|
||||||
|
case 'modify':
|
||||||
|
return '✏️';
|
||||||
|
case 'delete':
|
||||||
|
return '❌';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
229
ts/mod_format/classes.formatstats.ts
Normal file
229
ts/mod_format/classes.formatstats.ts
Normal file
@@ -0,0 +1,229 @@
|
|||||||
|
import * as plugins from './mod.plugins.js';
|
||||||
|
import { logger } from '../gitzone.logging.js';
|
||||||
|
|
||||||
|
export interface IModuleStats {
|
||||||
|
name: string;
|
||||||
|
filesProcessed: number;
|
||||||
|
executionTime: number;
|
||||||
|
errors: number;
|
||||||
|
successes: number;
|
||||||
|
filesCreated: number;
|
||||||
|
filesModified: number;
|
||||||
|
filesDeleted: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IFormatStats {
|
||||||
|
totalExecutionTime: number;
|
||||||
|
startTime: number;
|
||||||
|
endTime: number;
|
||||||
|
moduleStats: Map<string, IModuleStats>;
|
||||||
|
overallStats: {
|
||||||
|
totalFiles: number;
|
||||||
|
totalCreated: number;
|
||||||
|
totalModified: number;
|
||||||
|
totalDeleted: number;
|
||||||
|
totalErrors: number;
|
||||||
|
cacheHits: number;
|
||||||
|
cacheMisses: number;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export class FormatStats {
|
||||||
|
private stats: IFormatStats;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.stats = {
|
||||||
|
totalExecutionTime: 0,
|
||||||
|
startTime: Date.now(),
|
||||||
|
endTime: 0,
|
||||||
|
moduleStats: new Map(),
|
||||||
|
overallStats: {
|
||||||
|
totalFiles: 0,
|
||||||
|
totalCreated: 0,
|
||||||
|
totalModified: 0,
|
||||||
|
totalDeleted: 0,
|
||||||
|
totalErrors: 0,
|
||||||
|
cacheHits: 0,
|
||||||
|
cacheMisses: 0,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
startModule(moduleName: string): void {
|
||||||
|
this.stats.moduleStats.set(moduleName, {
|
||||||
|
name: moduleName,
|
||||||
|
filesProcessed: 0,
|
||||||
|
executionTime: 0,
|
||||||
|
errors: 0,
|
||||||
|
successes: 0,
|
||||||
|
filesCreated: 0,
|
||||||
|
filesModified: 0,
|
||||||
|
filesDeleted: 0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleStartTime(moduleName: string): number {
|
||||||
|
return Date.now();
|
||||||
|
}
|
||||||
|
|
||||||
|
endModule(moduleName: string, startTime: number): void {
|
||||||
|
const moduleStats = this.stats.moduleStats.get(moduleName);
|
||||||
|
if (moduleStats) {
|
||||||
|
moduleStats.executionTime = Date.now() - startTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
recordFileOperation(
|
||||||
|
moduleName: string,
|
||||||
|
operation: 'create' | 'modify' | 'delete',
|
||||||
|
success: boolean = true,
|
||||||
|
): void {
|
||||||
|
const moduleStats = this.stats.moduleStats.get(moduleName);
|
||||||
|
if (!moduleStats) return;
|
||||||
|
|
||||||
|
moduleStats.filesProcessed++;
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
moduleStats.successes++;
|
||||||
|
this.stats.overallStats.totalFiles++;
|
||||||
|
|
||||||
|
switch (operation) {
|
||||||
|
case 'create':
|
||||||
|
moduleStats.filesCreated++;
|
||||||
|
this.stats.overallStats.totalCreated++;
|
||||||
|
break;
|
||||||
|
case 'modify':
|
||||||
|
moduleStats.filesModified++;
|
||||||
|
this.stats.overallStats.totalModified++;
|
||||||
|
break;
|
||||||
|
case 'delete':
|
||||||
|
moduleStats.filesDeleted++;
|
||||||
|
this.stats.overallStats.totalDeleted++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
moduleStats.errors++;
|
||||||
|
this.stats.overallStats.totalErrors++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
recordCacheHit(): void {
|
||||||
|
this.stats.overallStats.cacheHits++;
|
||||||
|
}
|
||||||
|
|
||||||
|
recordCacheMiss(): void {
|
||||||
|
this.stats.overallStats.cacheMisses++;
|
||||||
|
}
|
||||||
|
|
||||||
|
finish(): void {
|
||||||
|
this.stats.endTime = Date.now();
|
||||||
|
this.stats.totalExecutionTime = this.stats.endTime - this.stats.startTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
displayStats(): void {
|
||||||
|
console.log('\n📊 Format Operation Statistics:');
|
||||||
|
console.log('═'.repeat(50));
|
||||||
|
|
||||||
|
// Overall stats
|
||||||
|
console.log('\nOverall Summary:');
|
||||||
|
console.log(
|
||||||
|
` Total Execution Time: ${this.formatDuration(this.stats.totalExecutionTime)}`,
|
||||||
|
);
|
||||||
|
console.log(` Files Processed: ${this.stats.overallStats.totalFiles}`);
|
||||||
|
console.log(` • Created: ${this.stats.overallStats.totalCreated}`);
|
||||||
|
console.log(` • Modified: ${this.stats.overallStats.totalModified}`);
|
||||||
|
console.log(` • Deleted: ${this.stats.overallStats.totalDeleted}`);
|
||||||
|
console.log(` Errors: ${this.stats.overallStats.totalErrors}`);
|
||||||
|
|
||||||
|
if (
|
||||||
|
this.stats.overallStats.cacheHits > 0 ||
|
||||||
|
this.stats.overallStats.cacheMisses > 0
|
||||||
|
) {
|
||||||
|
const cacheHitRate =
|
||||||
|
(this.stats.overallStats.cacheHits /
|
||||||
|
(this.stats.overallStats.cacheHits +
|
||||||
|
this.stats.overallStats.cacheMisses)) *
|
||||||
|
100;
|
||||||
|
console.log(` Cache Hit Rate: ${cacheHitRate.toFixed(1)}%`);
|
||||||
|
console.log(` • Hits: ${this.stats.overallStats.cacheHits}`);
|
||||||
|
console.log(` • Misses: ${this.stats.overallStats.cacheMisses}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Module stats
|
||||||
|
console.log('\nModule Breakdown:');
|
||||||
|
console.log('─'.repeat(50));
|
||||||
|
|
||||||
|
const sortedModules = Array.from(this.stats.moduleStats.values()).sort(
|
||||||
|
(a, b) => b.filesProcessed - a.filesProcessed,
|
||||||
|
);
|
||||||
|
|
||||||
|
for (const moduleStats of sortedModules) {
|
||||||
|
console.log(
|
||||||
|
`\n${this.getModuleIcon(moduleStats.name)} ${moduleStats.name}:`,
|
||||||
|
);
|
||||||
|
console.log(
|
||||||
|
` Execution Time: ${this.formatDuration(moduleStats.executionTime)}`,
|
||||||
|
);
|
||||||
|
console.log(` Files Processed: ${moduleStats.filesProcessed}`);
|
||||||
|
|
||||||
|
if (moduleStats.filesCreated > 0) {
|
||||||
|
console.log(` • Created: ${moduleStats.filesCreated}`);
|
||||||
|
}
|
||||||
|
if (moduleStats.filesModified > 0) {
|
||||||
|
console.log(` • Modified: ${moduleStats.filesModified}`);
|
||||||
|
}
|
||||||
|
if (moduleStats.filesDeleted > 0) {
|
||||||
|
console.log(` • Deleted: ${moduleStats.filesDeleted}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (moduleStats.errors > 0) {
|
||||||
|
console.log(` ❌ Errors: ${moduleStats.errors}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('\n' + '═'.repeat(50));
|
||||||
|
}
|
||||||
|
|
||||||
|
async saveReport(outputPath: string): Promise<void> {
|
||||||
|
const report = {
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
executionTime: this.stats.totalExecutionTime,
|
||||||
|
overallStats: this.stats.overallStats,
|
||||||
|
moduleStats: Array.from(this.stats.moduleStats.values()),
|
||||||
|
};
|
||||||
|
|
||||||
|
await plugins.smartfile.memory.toFs(
|
||||||
|
JSON.stringify(report, null, 2),
|
||||||
|
outputPath,
|
||||||
|
);
|
||||||
|
logger.log('info', `Statistics report saved to ${outputPath}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
private formatDuration(ms: number): string {
|
||||||
|
if (ms < 1000) {
|
||||||
|
return `${ms}ms`;
|
||||||
|
} else if (ms < 60000) {
|
||||||
|
return `${(ms / 1000).toFixed(1)}s`;
|
||||||
|
} else {
|
||||||
|
const minutes = Math.floor(ms / 60000);
|
||||||
|
const seconds = Math.floor((ms % 60000) / 1000);
|
||||||
|
return `${minutes}m ${seconds}s`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private getModuleIcon(module: string): string {
|
||||||
|
const icons: Record<string, string> = {
|
||||||
|
packagejson: '📦',
|
||||||
|
license: '📝',
|
||||||
|
tsconfig: '🔧',
|
||||||
|
cleanup: '🚮',
|
||||||
|
gitignore: '🔒',
|
||||||
|
prettier: '✨',
|
||||||
|
readme: '📖',
|
||||||
|
templates: '📄',
|
||||||
|
npmextra: '⚙️',
|
||||||
|
copy: '📋',
|
||||||
|
};
|
||||||
|
return icons[module] || '📁';
|
||||||
|
}
|
||||||
|
}
|
318
ts/mod_format/classes.rollbackmanager.ts
Normal file
318
ts/mod_format/classes.rollbackmanager.ts
Normal file
@@ -0,0 +1,318 @@
|
|||||||
|
import * as plugins from './mod.plugins.js';
|
||||||
|
import * as paths from '../paths.js';
|
||||||
|
import type { IFormatOperation } from './interfaces.format.js';
|
||||||
|
|
||||||
|
export class RollbackManager {
|
||||||
|
private backupDir: string;
|
||||||
|
private manifestPath: string;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.backupDir = plugins.path.join(paths.cwd, '.nogit', 'gitzone-backups');
|
||||||
|
this.manifestPath = plugins.path.join(this.backupDir, 'manifest.json');
|
||||||
|
}
|
||||||
|
|
||||||
|
async createOperation(): Promise<IFormatOperation> {
|
||||||
|
await this.ensureBackupDir();
|
||||||
|
|
||||||
|
const operation: IFormatOperation = {
|
||||||
|
id: this.generateOperationId(),
|
||||||
|
timestamp: Date.now(),
|
||||||
|
files: [],
|
||||||
|
status: 'pending',
|
||||||
|
};
|
||||||
|
|
||||||
|
await this.updateManifest(operation);
|
||||||
|
return operation;
|
||||||
|
}
|
||||||
|
|
||||||
|
async backupFile(filepath: string, operationId: string): Promise<void> {
|
||||||
|
const operation = await this.getOperation(operationId);
|
||||||
|
if (!operation) {
|
||||||
|
throw new Error(`Operation ${operationId} not found`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const absolutePath = plugins.path.isAbsolute(filepath)
|
||||||
|
? filepath
|
||||||
|
: plugins.path.join(paths.cwd, filepath);
|
||||||
|
|
||||||
|
// Check if file exists
|
||||||
|
const exists = await plugins.smartfile.fs.fileExists(absolutePath);
|
||||||
|
if (!exists) {
|
||||||
|
// File doesn't exist yet (will be created), so we skip backup
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read file content and metadata
|
||||||
|
const content = plugins.smartfile.fs.toStringSync(absolutePath);
|
||||||
|
const stats = await plugins.smartfile.fs.stat(absolutePath);
|
||||||
|
const checksum = this.calculateChecksum(content);
|
||||||
|
|
||||||
|
// Create backup
|
||||||
|
const backupPath = this.getBackupPath(operationId, filepath);
|
||||||
|
await plugins.smartfile.fs.ensureDir(plugins.path.dirname(backupPath));
|
||||||
|
await plugins.smartfile.memory.toFs(content, backupPath);
|
||||||
|
|
||||||
|
// Update operation
|
||||||
|
operation.files.push({
|
||||||
|
path: filepath,
|
||||||
|
originalContent: content,
|
||||||
|
checksum,
|
||||||
|
permissions: stats.mode.toString(8),
|
||||||
|
});
|
||||||
|
|
||||||
|
await this.updateManifest(operation);
|
||||||
|
}
|
||||||
|
|
||||||
|
async rollback(operationId: string): Promise<void> {
|
||||||
|
const operation = await this.getOperation(operationId);
|
||||||
|
if (!operation) {
|
||||||
|
// Operation doesn't exist, might have already been rolled back or never created
|
||||||
|
console.warn(`Operation ${operationId} not found for rollback, skipping`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (operation.status === 'rolled-back') {
|
||||||
|
throw new Error(`Operation ${operationId} has already been rolled back`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore files in reverse order
|
||||||
|
for (let i = operation.files.length - 1; i >= 0; i--) {
|
||||||
|
const file = operation.files[i];
|
||||||
|
const absolutePath = plugins.path.isAbsolute(file.path)
|
||||||
|
? file.path
|
||||||
|
: plugins.path.join(paths.cwd, file.path);
|
||||||
|
|
||||||
|
// Verify backup integrity
|
||||||
|
const backupPath = this.getBackupPath(operationId, file.path);
|
||||||
|
const backupContent = plugins.smartfile.fs.toStringSync(backupPath);
|
||||||
|
const backupChecksum = this.calculateChecksum(backupContent);
|
||||||
|
|
||||||
|
if (backupChecksum !== file.checksum) {
|
||||||
|
throw new Error(`Backup integrity check failed for ${file.path}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore file
|
||||||
|
await plugins.smartfile.memory.toFs(file.originalContent, absolutePath);
|
||||||
|
|
||||||
|
// Restore permissions
|
||||||
|
const mode = parseInt(file.permissions, 8);
|
||||||
|
// Note: Permissions restoration may not work on all platforms
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update operation status
|
||||||
|
operation.status = 'rolled-back';
|
||||||
|
await this.updateManifest(operation);
|
||||||
|
}
|
||||||
|
|
||||||
|
async markComplete(operationId: string): Promise<void> {
|
||||||
|
const operation = await this.getOperation(operationId);
|
||||||
|
if (!operation) {
|
||||||
|
throw new Error(`Operation ${operationId} not found`);
|
||||||
|
}
|
||||||
|
|
||||||
|
operation.status = 'completed';
|
||||||
|
await this.updateManifest(operation);
|
||||||
|
}
|
||||||
|
|
||||||
|
async cleanOldBackups(retentionDays: number): Promise<void> {
|
||||||
|
const manifest = await this.getManifest();
|
||||||
|
const cutoffTime = Date.now() - retentionDays * 24 * 60 * 60 * 1000;
|
||||||
|
|
||||||
|
const operationsToDelete = manifest.operations.filter(
|
||||||
|
(op) => op.timestamp < cutoffTime && op.status === 'completed',
|
||||||
|
);
|
||||||
|
|
||||||
|
for (const operation of operationsToDelete) {
|
||||||
|
// Remove backup files
|
||||||
|
const operationDir = plugins.path.join(
|
||||||
|
this.backupDir,
|
||||||
|
'operations',
|
||||||
|
operation.id,
|
||||||
|
);
|
||||||
|
await plugins.smartfile.fs.remove(operationDir);
|
||||||
|
|
||||||
|
// Remove from manifest
|
||||||
|
manifest.operations = manifest.operations.filter(
|
||||||
|
(op) => op.id !== operation.id,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.saveManifest(manifest);
|
||||||
|
}
|
||||||
|
|
||||||
|
async verifyBackup(operationId: string): Promise<boolean> {
|
||||||
|
const operation = await this.getOperation(operationId);
|
||||||
|
if (!operation) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const file of operation.files) {
|
||||||
|
const backupPath = this.getBackupPath(operationId, file.path);
|
||||||
|
const exists = await plugins.smartfile.fs.fileExists(backupPath);
|
||||||
|
|
||||||
|
if (!exists) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const content = plugins.smartfile.fs.toStringSync(backupPath);
|
||||||
|
const checksum = this.calculateChecksum(content);
|
||||||
|
|
||||||
|
if (checksum !== file.checksum) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async listBackups(): Promise<IFormatOperation[]> {
|
||||||
|
const manifest = await this.getManifest();
|
||||||
|
return manifest.operations;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async ensureBackupDir(): Promise<void> {
|
||||||
|
await plugins.smartfile.fs.ensureDir(this.backupDir);
|
||||||
|
await plugins.smartfile.fs.ensureDir(
|
||||||
|
plugins.path.join(this.backupDir, 'operations'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private generateOperationId(): string {
|
||||||
|
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
||||||
|
const random = Math.random().toString(36).substring(2, 8);
|
||||||
|
return `${timestamp}-${random}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private getBackupPath(operationId: string, filepath: string): string {
|
||||||
|
const filename = plugins.path.basename(filepath);
|
||||||
|
const dir = plugins.path.dirname(filepath);
|
||||||
|
const safeDir = dir.replace(/[/\\]/g, '__');
|
||||||
|
return plugins.path.join(
|
||||||
|
this.backupDir,
|
||||||
|
'operations',
|
||||||
|
operationId,
|
||||||
|
'files',
|
||||||
|
safeDir,
|
||||||
|
`${filename}.backup`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private calculateChecksum(content: string | Buffer): string {
|
||||||
|
return plugins.crypto.createHash('sha256').update(content).digest('hex');
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getManifest(): Promise<{ operations: IFormatOperation[] }> {
|
||||||
|
const defaultManifest = { operations: [] };
|
||||||
|
|
||||||
|
const exists = await plugins.smartfile.fs.fileExists(this.manifestPath);
|
||||||
|
if (!exists) {
|
||||||
|
return defaultManifest;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const content = plugins.smartfile.fs.toStringSync(this.manifestPath);
|
||||||
|
const manifest = JSON.parse(content);
|
||||||
|
|
||||||
|
// Validate the manifest structure
|
||||||
|
if (this.isValidManifest(manifest)) {
|
||||||
|
return manifest;
|
||||||
|
} else {
|
||||||
|
console.warn(
|
||||||
|
'Invalid rollback manifest structure, returning default manifest',
|
||||||
|
);
|
||||||
|
return defaultManifest;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(
|
||||||
|
`Failed to read rollback manifest: ${error.message}, returning default manifest`,
|
||||||
|
);
|
||||||
|
// Try to delete the corrupted file
|
||||||
|
try {
|
||||||
|
await plugins.smartfile.fs.remove(this.manifestPath);
|
||||||
|
} catch (removeError) {
|
||||||
|
// Ignore removal errors
|
||||||
|
}
|
||||||
|
return defaultManifest;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async saveManifest(manifest: {
|
||||||
|
operations: IFormatOperation[];
|
||||||
|
}): Promise<void> {
|
||||||
|
// Validate before saving
|
||||||
|
if (!this.isValidManifest(manifest)) {
|
||||||
|
throw new Error('Invalid rollback manifest structure, cannot save');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure directory exists
|
||||||
|
await this.ensureBackupDir();
|
||||||
|
|
||||||
|
// Write directly with proper JSON stringification
|
||||||
|
const jsonContent = JSON.stringify(manifest, null, 2);
|
||||||
|
await plugins.smartfile.memory.toFs(jsonContent, this.manifestPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getOperation(
|
||||||
|
operationId: string,
|
||||||
|
): Promise<IFormatOperation | null> {
|
||||||
|
const manifest = await this.getManifest();
|
||||||
|
return manifest.operations.find((op) => op.id === operationId) || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async updateManifest(operation: IFormatOperation): Promise<void> {
|
||||||
|
const manifest = await this.getManifest();
|
||||||
|
const existingIndex = manifest.operations.findIndex(
|
||||||
|
(op) => op.id === operation.id,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (existingIndex !== -1) {
|
||||||
|
manifest.operations[existingIndex] = operation;
|
||||||
|
} else {
|
||||||
|
manifest.operations.push(operation);
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.saveManifest(manifest);
|
||||||
|
}
|
||||||
|
|
||||||
|
private isValidManifest(
|
||||||
|
manifest: any,
|
||||||
|
): manifest is { operations: IFormatOperation[] } {
|
||||||
|
// Check if manifest has the required structure
|
||||||
|
if (!manifest || typeof manifest !== 'object') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check required fields
|
||||||
|
if (!Array.isArray(manifest.operations)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check each operation entry
|
||||||
|
for (const operation of manifest.operations) {
|
||||||
|
if (
|
||||||
|
!operation ||
|
||||||
|
typeof operation !== 'object' ||
|
||||||
|
typeof operation.id !== 'string' ||
|
||||||
|
typeof operation.timestamp !== 'number' ||
|
||||||
|
typeof operation.status !== 'string' ||
|
||||||
|
!Array.isArray(operation.files)
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check each file in the operation
|
||||||
|
for (const file of operation.files) {
|
||||||
|
if (
|
||||||
|
!file ||
|
||||||
|
typeof file !== 'object' ||
|
||||||
|
typeof file.path !== 'string' ||
|
||||||
|
typeof file.checksum !== 'string'
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
@@ -4,14 +4,21 @@ import * as paths from '../paths.js';
|
|||||||
import { logger } from '../gitzone.logging.js';
|
import { logger } from '../gitzone.logging.js';
|
||||||
import { Project } from '../classes.project.js';
|
import { Project } from '../classes.project.js';
|
||||||
|
|
||||||
const filesToDelete = ['defaults.yml', 'yarn.lock', 'package-lock.json', 'tslint.json'];
|
const filesToDelete = [
|
||||||
|
'defaults.yml',
|
||||||
|
'yarn.lock',
|
||||||
|
'package-lock.json',
|
||||||
|
'tslint.json',
|
||||||
|
];
|
||||||
|
|
||||||
export const run = async (projectArg: Project) => {
|
export const run = async (projectArg: Project) => {
|
||||||
for (const relativeFilePath of filesToDelete) {
|
for (const relativeFilePath of filesToDelete) {
|
||||||
const fileExists = plugins.smartfile.fs.fileExistsSync(relativeFilePath);
|
const fileExists = plugins.smartfile.fs.fileExistsSync(relativeFilePath);
|
||||||
if (fileExists) {
|
if (fileExists) {
|
||||||
logger.log('info', `Found ${relativeFilePath}! Removing it!`);
|
logger.log('info', `Found ${relativeFilePath}! Removing it!`);
|
||||||
plugins.smartfile.fs.removeSync(plugins.path.join(paths.cwd, relativeFilePath));
|
plugins.smartfile.fs.removeSync(
|
||||||
|
plugins.path.join(paths.cwd, relativeFilePath),
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
logger.log('info', `Project is free of ${relativeFilePath}`);
|
logger.log('info', `Project is free of ${relativeFilePath}`);
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,85 @@
|
|||||||
import type { Project } from '../classes.project.js';
|
import type { Project } from '../classes.project.js';
|
||||||
import * as plugins from '../plugins.js';
|
import * as plugins from './mod.plugins.js';
|
||||||
|
import { logger } from '../gitzone.logging.js';
|
||||||
|
|
||||||
export const run = async (projectArg: Project) => {
|
export const run = async (projectArg: Project) => {
|
||||||
const gitzoneConfig = await projectArg.gitzoneConfig;
|
const gitzoneConfig = await projectArg.gitzoneConfig;
|
||||||
|
|
||||||
|
// Get copy configuration from npmextra.json
|
||||||
|
const npmextraConfig = new plugins.npmextra.Npmextra();
|
||||||
|
const copyConfig = npmextraConfig.dataFor<any>('gitzone.format.copy', {
|
||||||
|
patterns: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!copyConfig.patterns || copyConfig.patterns.length === 0) {
|
||||||
|
logger.log('info', 'No copy patterns configured in npmextra.json');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const pattern of copyConfig.patterns) {
|
||||||
|
if (!pattern.from || !pattern.to) {
|
||||||
|
logger.log('warn', 'Invalid copy pattern - missing "from" or "to" field');
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Handle glob patterns
|
||||||
|
const files = await plugins.smartfile.fs.listFileTree('.', pattern.from);
|
||||||
|
|
||||||
|
for (const file of files) {
|
||||||
|
const sourcePath = file;
|
||||||
|
let destPath = pattern.to;
|
||||||
|
|
||||||
|
// If destination is a directory, preserve filename
|
||||||
|
if (pattern.to.endsWith('/')) {
|
||||||
|
const filename = plugins.path.basename(file);
|
||||||
|
destPath = plugins.path.join(pattern.to, filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle template variables in destination path
|
||||||
|
if (pattern.preservePath) {
|
||||||
|
const relativePath = plugins.path.relative(
|
||||||
|
plugins.path.dirname(pattern.from.replace(/\*/g, '')),
|
||||||
|
file,
|
||||||
|
);
|
||||||
|
destPath = plugins.path.join(pattern.to, relativePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure destination directory exists
|
||||||
|
await plugins.smartfile.fs.ensureDir(plugins.path.dirname(destPath));
|
||||||
|
|
||||||
|
// Copy file
|
||||||
|
await plugins.smartfile.fs.copy(sourcePath, destPath);
|
||||||
|
logger.log('info', `Copied ${sourcePath} to ${destPath}`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
logger.log(
|
||||||
|
'error',
|
||||||
|
`Failed to copy pattern ${pattern.from}: ${error.message}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Example npmextra.json configuration:
|
||||||
|
* {
|
||||||
|
* "gitzone": {
|
||||||
|
* "format": {
|
||||||
|
* "copy": {
|
||||||
|
* "patterns": [
|
||||||
|
* {
|
||||||
|
* "from": "src/assets/*",
|
||||||
|
* "to": "dist/assets/",
|
||||||
|
* "preservePath": true
|
||||||
|
* },
|
||||||
|
* {
|
||||||
|
* "from": "config/*.json",
|
||||||
|
* "to": "dist/"
|
||||||
|
* }
|
||||||
|
* ]
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
@@ -12,7 +12,8 @@ export const run = async (projectArg: Project) => {
|
|||||||
const ciTemplate = await templateModule.getTemplate('gitignore');
|
const ciTemplate = await templateModule.getTemplate('gitignore');
|
||||||
if (gitignoreExists) {
|
if (gitignoreExists) {
|
||||||
// lets get the existing gitignore file
|
// lets get the existing gitignore file
|
||||||
const existingGitIgnoreString = plugins.smartfile.fs.toStringSync(gitignorePath);
|
const existingGitIgnoreString =
|
||||||
|
plugins.smartfile.fs.toStringSync(gitignorePath);
|
||||||
let customPart = existingGitIgnoreString.split('# custom\n')[1];
|
let customPart = existingGitIgnoreString.split('# custom\n')[1];
|
||||||
customPart ? null : (customPart = '');
|
customPart ? null : (customPart = '');
|
||||||
}
|
}
|
||||||
|
@@ -24,7 +24,9 @@ export const run = async (projectArg: Project) => {
|
|||||||
} else {
|
} else {
|
||||||
logger.log('error', 'Error -> licenses failed. Here is why:');
|
logger.log('error', 'Error -> licenses failed. Here is why:');
|
||||||
for (const failedModule of licenseCheckResult.failingModules) {
|
for (const failedModule of licenseCheckResult.failingModules) {
|
||||||
console.log(`${failedModule.name} fails with license ${failedModule.license}`);
|
console.log(
|
||||||
|
`${failedModule.name} fails with license ${failedModule.license}`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@@ -29,7 +29,12 @@ export const run = async (projectArg: Project) => {
|
|||||||
|
|
||||||
const interactInstance = new plugins.smartinteract.SmartInteract();
|
const interactInstance = new plugins.smartinteract.SmartInteract();
|
||||||
for (const expectedRepoInformationItem of expectedRepoInformation) {
|
for (const expectedRepoInformationItem of expectedRepoInformation) {
|
||||||
if (!plugins.smartobject.smartGet(npmextraJson.gitzone, expectedRepoInformationItem)) {
|
if (
|
||||||
|
!plugins.smartobject.smartGet(
|
||||||
|
npmextraJson.gitzone,
|
||||||
|
expectedRepoInformationItem,
|
||||||
|
)
|
||||||
|
) {
|
||||||
interactInstance.addQuestions([
|
interactInstance.addQuestions([
|
||||||
{
|
{
|
||||||
message: `What is the value of ${expectedRepoInformationItem}`,
|
message: `What is the value of ${expectedRepoInformationItem}`,
|
||||||
@@ -43,7 +48,9 @@ export const run = async (projectArg: Project) => {
|
|||||||
|
|
||||||
const answerbucket = await interactInstance.runQueue();
|
const answerbucket = await interactInstance.runQueue();
|
||||||
for (const expectedRepoInformationItem of expectedRepoInformation) {
|
for (const expectedRepoInformationItem of expectedRepoInformation) {
|
||||||
const cliProvidedValue = answerbucket.getAnswerFor(expectedRepoInformationItem);
|
const cliProvidedValue = answerbucket.getAnswerFor(
|
||||||
|
expectedRepoInformationItem,
|
||||||
|
);
|
||||||
if (cliProvidedValue) {
|
if (cliProvidedValue) {
|
||||||
plugins.smartobject.smartAdd(
|
plugins.smartobject.smartAdd(
|
||||||
npmextraJson.gitzone,
|
npmextraJson.gitzone,
|
||||||
|
@@ -13,7 +13,61 @@ const ensureDependency = async (
|
|||||||
position: 'dep' | 'devDep' | 'everywhere',
|
position: 'dep' | 'devDep' | 'everywhere',
|
||||||
constraint: 'exclude' | 'include' | 'latest',
|
constraint: 'exclude' | 'include' | 'latest',
|
||||||
dependencyArg: string,
|
dependencyArg: string,
|
||||||
) => {};
|
) => {
|
||||||
|
const [packageName, version] = dependencyArg.includes('@')
|
||||||
|
? dependencyArg.split('@').filter(Boolean)
|
||||||
|
: [dependencyArg, 'latest'];
|
||||||
|
|
||||||
|
const targetSections: string[] = [];
|
||||||
|
|
||||||
|
switch (position) {
|
||||||
|
case 'dep':
|
||||||
|
targetSections.push('dependencies');
|
||||||
|
break;
|
||||||
|
case 'devDep':
|
||||||
|
targetSections.push('devDependencies');
|
||||||
|
break;
|
||||||
|
case 'everywhere':
|
||||||
|
targetSections.push('dependencies', 'devDependencies');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const section of targetSections) {
|
||||||
|
if (!packageJsonObjectArg[section]) {
|
||||||
|
packageJsonObjectArg[section] = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (constraint) {
|
||||||
|
case 'exclude':
|
||||||
|
delete packageJsonObjectArg[section][packageName];
|
||||||
|
break;
|
||||||
|
case 'include':
|
||||||
|
if (!packageJsonObjectArg[section][packageName]) {
|
||||||
|
packageJsonObjectArg[section][packageName] =
|
||||||
|
version === 'latest' ? '^1.0.0' : version;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'latest':
|
||||||
|
// Fetch latest version from npm
|
||||||
|
try {
|
||||||
|
const registry = new plugins.smartnpm.NpmRegistry();
|
||||||
|
const packageInfo = await registry.getPackageInfo(packageName);
|
||||||
|
const latestVersion = packageInfo['dist-tags'].latest;
|
||||||
|
packageJsonObjectArg[section][packageName] = `^${latestVersion}`;
|
||||||
|
} catch (error) {
|
||||||
|
logger.log(
|
||||||
|
'warn',
|
||||||
|
`Could not fetch latest version for ${packageName}, using existing or default`,
|
||||||
|
);
|
||||||
|
if (!packageJsonObjectArg[section][packageName]) {
|
||||||
|
packageJsonObjectArg[section][packageName] =
|
||||||
|
version === 'latest' ? '^1.0.0' : version;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export const run = async (projectArg: Project) => {
|
export const run = async (projectArg: Project) => {
|
||||||
const formatStreamWrapper = new plugins.smartstream.StreamWrapper([
|
const formatStreamWrapper = new plugins.smartstream.StreamWrapper([
|
||||||
@@ -27,7 +81,7 @@ export const run = async (projectArg: Project) => {
|
|||||||
// metadata
|
// metadata
|
||||||
packageJson.repository = {
|
packageJson.repository = {
|
||||||
type: 'git',
|
type: 'git',
|
||||||
url: `git+https://${gitzoneData.module.githost}/${gitzoneData.module.gitscope}/${gitzoneData.module.gitrepo}.git`,
|
url: `https://${gitzoneData.module.githost}/${gitzoneData.module.gitscope}/${gitzoneData.module.gitrepo}.git`,
|
||||||
};
|
};
|
||||||
(packageJson.bugs = {
|
(packageJson.bugs = {
|
||||||
url: `https://${gitzoneData.module.githost}/${gitzoneData.module.gitscope}/${gitzoneData.module.gitrepo}/issues`,
|
url: `https://${gitzoneData.module.githost}/${gitzoneData.module.gitscope}/${gitzoneData.module.gitrepo}/issues`,
|
||||||
@@ -42,9 +96,15 @@ export const run = async (projectArg: Project) => {
|
|||||||
|
|
||||||
// Check for private or public
|
// Check for private or public
|
||||||
if (packageJson.private !== undefined) {
|
if (packageJson.private !== undefined) {
|
||||||
logger.log('info', 'Success -> found private/public info in package.json!');
|
logger.log(
|
||||||
|
'info',
|
||||||
|
'Success -> found private/public info in package.json!',
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
logger.log('error', 'found no private boolean! Setting it to private for now!');
|
logger.log(
|
||||||
|
'error',
|
||||||
|
'found no private boolean! Setting it to private for now!',
|
||||||
|
);
|
||||||
packageJson.private = true;
|
packageJson.private = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,7 +112,10 @@ export const run = async (projectArg: Project) => {
|
|||||||
if (packageJson.license) {
|
if (packageJson.license) {
|
||||||
logger.log('info', 'Success -> found license in package.json!');
|
logger.log('info', 'Success -> found license in package.json!');
|
||||||
} else {
|
} else {
|
||||||
logger.log('error', 'found no license! Setting it to UNLICENSED for now!');
|
logger.log(
|
||||||
|
'error',
|
||||||
|
'found no license! Setting it to UNLICENSED for now!',
|
||||||
|
);
|
||||||
packageJson.license = 'UNLICENSED';
|
packageJson.license = 'UNLICENSED';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,13 +123,19 @@ export const run = async (projectArg: Project) => {
|
|||||||
if (packageJson.scripts.build) {
|
if (packageJson.scripts.build) {
|
||||||
logger.log('info', 'Success -> found build script in package.json!');
|
logger.log('info', 'Success -> found build script in package.json!');
|
||||||
} else {
|
} else {
|
||||||
logger.log('error', 'found no build script! Putting a placeholder there for now!');
|
logger.log(
|
||||||
|
'error',
|
||||||
|
'found no build script! Putting a placeholder there for now!',
|
||||||
|
);
|
||||||
packageJson.scripts.build = `echo "Not needed for now"`;
|
packageJson.scripts.build = `echo "Not needed for now"`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for buildDocs script
|
// Check for buildDocs script
|
||||||
if (!packageJson.scripts.buildDocs) {
|
if (!packageJson.scripts.buildDocs) {
|
||||||
logger.log('info', 'found no buildDocs script! Putting tsdoc script there now.');
|
logger.log(
|
||||||
|
'info',
|
||||||
|
'found no buildDocs script! Putting tsdoc script there now.',
|
||||||
|
);
|
||||||
packageJson.scripts.buildDocs = `tsdoc`;
|
packageJson.scripts.buildDocs = `tsdoc`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,9 +154,31 @@ export const run = async (projectArg: Project) => {
|
|||||||
];
|
];
|
||||||
|
|
||||||
// check for dependencies
|
// check for dependencies
|
||||||
await ensureDependency(packageJson, 'devDep', 'latest', '@push.rocks/tapbundle');
|
await ensureDependency(
|
||||||
await ensureDependency(packageJson, 'devDep', 'latest', '@git.zone/tstest');
|
packageJson,
|
||||||
await ensureDependency(packageJson, 'devDep', 'latest', '@git.zone/tsbuild');
|
'devDep',
|
||||||
|
'latest',
|
||||||
|
'@push.rocks/tapbundle',
|
||||||
|
);
|
||||||
|
await ensureDependency(
|
||||||
|
packageJson,
|
||||||
|
'devDep',
|
||||||
|
'latest',
|
||||||
|
'@git.zone/tstest',
|
||||||
|
);
|
||||||
|
await ensureDependency(
|
||||||
|
packageJson,
|
||||||
|
'devDep',
|
||||||
|
'latest',
|
||||||
|
'@git.zone/tsbuild',
|
||||||
|
);
|
||||||
|
|
||||||
|
// set overrides
|
||||||
|
const overrides = plugins.smartfile.fs.toObjectSync(
|
||||||
|
plugins.path.join(paths.assetsDir, 'overrides.json'),
|
||||||
|
);
|
||||||
|
packageJson.pnpm = packageJson.pnpm || {};
|
||||||
|
packageJson.pnpm.overrides = overrides;
|
||||||
|
|
||||||
// exclude
|
// exclude
|
||||||
// TODO
|
// TODO
|
||||||
|
@@ -16,7 +16,12 @@ const prettierDefaultMarkdownConfig: prettier.Options = {
|
|||||||
parser: 'markdown',
|
parser: 'markdown',
|
||||||
};
|
};
|
||||||
|
|
||||||
const filesToFormat = [`ts/**/*.ts`, `test/**/*.ts`, `readme.md`, `docs/**/*.md`];
|
const filesToFormat = [
|
||||||
|
`ts/**/*.ts`,
|
||||||
|
`test/**/*.ts`,
|
||||||
|
`readme.md`,
|
||||||
|
`docs/**/*.md`,
|
||||||
|
];
|
||||||
|
|
||||||
const choosePrettierConfig = (fileArg: plugins.smartfile.SmartFile) => {
|
const choosePrettierConfig = (fileArg: plugins.smartfile.SmartFile) => {
|
||||||
switch (fileArg.parsedPath.ext) {
|
switch (fileArg.parsedPath.ext) {
|
||||||
@@ -39,7 +44,10 @@ const prettierTypeScriptPipestop = plugins.through2.obj(
|
|||||||
cb(null);
|
cb(null);
|
||||||
} else {
|
} else {
|
||||||
logger.log('info', `${fileArg.path} is being reformated!`);
|
logger.log('info', `${fileArg.path} is being reformated!`);
|
||||||
const formatedFileString = await prettier.format(fileString, chosenConfig);
|
const formatedFileString = await prettier.format(
|
||||||
|
fileString,
|
||||||
|
chosenConfig,
|
||||||
|
);
|
||||||
fileArg.setContentsFromString(formatedFileString);
|
fileArg.setContentsFromString(formatedFileString);
|
||||||
cb(null, fileArg);
|
cb(null, fileArg);
|
||||||
}
|
}
|
||||||
|
@@ -18,7 +18,8 @@ export const run = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check and initialize readme.hints.md if it doesn't exist
|
// Check and initialize readme.hints.md if it doesn't exist
|
||||||
const readmeHintsExists = await plugins.smartfile.fs.fileExists(readmeHintsPath);
|
const readmeHintsExists =
|
||||||
|
await plugins.smartfile.fs.fileExists(readmeHintsPath);
|
||||||
if (!readmeHintsExists) {
|
if (!readmeHintsExists) {
|
||||||
await plugins.smartfile.fs.toFs(
|
await plugins.smartfile.fs.toFs(
|
||||||
'# Project Readme Hints\n\nThis is the initial readme hints file.',
|
'# Project Readme Hints\n\nThis is the initial readme hints file.',
|
||||||
|
@@ -26,10 +26,12 @@ export const run = async (project: Project) => {
|
|||||||
case 'npm':
|
case 'npm':
|
||||||
case 'wcc':
|
case 'wcc':
|
||||||
if (project.gitzoneConfig.data.npmciOptions.npmAccessLevel === 'public') {
|
if (project.gitzoneConfig.data.npmciOptions.npmAccessLevel === 'public') {
|
||||||
const ciTemplateDefault = await templateModule.getTemplate('ci_default');
|
const ciTemplateDefault =
|
||||||
|
await templateModule.getTemplate('ci_default');
|
||||||
ciTemplateDefault.writeToDisk(paths.cwd);
|
ciTemplateDefault.writeToDisk(paths.cwd);
|
||||||
} else {
|
} else {
|
||||||
const ciTemplateDefault = await templateModule.getTemplate('ci_default_private');
|
const ciTemplateDefault =
|
||||||
|
await templateModule.getTemplate('ci_default_private');
|
||||||
ciTemplateDefault.writeToDisk(paths.cwd);
|
ciTemplateDefault.writeToDisk(paths.cwd);
|
||||||
}
|
}
|
||||||
logger.log('info', 'Updated .gitlabci.yml!');
|
logger.log('info', 'Updated .gitlabci.yml!');
|
||||||
@@ -38,10 +40,11 @@ export const run = async (project: Project) => {
|
|||||||
case 'website':
|
case 'website':
|
||||||
const ciTemplateDocker = await templateModule.getTemplate('ci_docker');
|
const ciTemplateDocker = await templateModule.getTemplate('ci_docker');
|
||||||
await ciTemplateDocker.writeToDisk(paths.cwd);
|
await ciTemplateDocker.writeToDisk(paths.cwd);
|
||||||
logger.log('info', 'Updated .gitlabci.yml!');
|
logger.log('info', 'Updated CI/CD config files!');
|
||||||
|
|
||||||
// lets care about docker
|
// lets care about docker
|
||||||
const dockerTemplate = await templateModule.getTemplate('dockerfile_service');
|
const dockerTemplate =
|
||||||
|
await templateModule.getTemplate('dockerfile_service');
|
||||||
dockerTemplate.writeToDisk(paths.cwd);
|
dockerTemplate.writeToDisk(paths.cwd);
|
||||||
logger.log('info', 'Updated Dockerfile!');
|
logger.log('info', 'Updated Dockerfile!');
|
||||||
|
|
||||||
@@ -56,11 +59,22 @@ export const run = async (project: Project) => {
|
|||||||
|
|
||||||
// update html
|
// update html
|
||||||
if (project.gitzoneConfig.data.projectType === 'website') {
|
if (project.gitzoneConfig.data.projectType === 'website') {
|
||||||
const websiteUpdateTemplate = await templateModule.getTemplate('website_update');
|
const websiteUpdateTemplate =
|
||||||
|
await templateModule.getTemplate('website_update');
|
||||||
|
const variables = {
|
||||||
|
assetbrokerUrl: project.gitzoneConfig.data.module.assetbrokerUrl,
|
||||||
|
legalUrl: project.gitzoneConfig.data.module.legalUrl,
|
||||||
|
};
|
||||||
|
console.log(
|
||||||
|
'updating website template with variables\n',
|
||||||
|
JSON.stringify(variables, null, 2),
|
||||||
|
);
|
||||||
|
websiteUpdateTemplate.supplyVariables(variables);
|
||||||
await websiteUpdateTemplate.writeToDisk(paths.cwd);
|
await websiteUpdateTemplate.writeToDisk(paths.cwd);
|
||||||
logger.log('info', `Updated html for website!`);
|
logger.log('info', `Updated html for website!`);
|
||||||
} else if (project.gitzoneConfig.data.projectType === 'service') {
|
} else if (project.gitzoneConfig.data.projectType === 'service') {
|
||||||
const websiteUpdateTemplate = await templateModule.getTemplate('service_update');
|
const websiteUpdateTemplate =
|
||||||
|
await templateModule.getTemplate('service_update');
|
||||||
await websiteUpdateTemplate.writeToDisk(paths.cwd);
|
await websiteUpdateTemplate.writeToDisk(paths.cwd);
|
||||||
logger.log('info', `Updated html for element template!`);
|
logger.log('info', `Updated html for element template!`);
|
||||||
} else if (project.gitzoneConfig.data.projectType === 'wcc') {
|
} else if (project.gitzoneConfig.data.projectType === 'wcc') {
|
||||||
|
@@ -6,7 +6,10 @@ import { Project } from '../classes.project.js';
|
|||||||
|
|
||||||
export const run = async (projectArg: Project) => {
|
export const run = async (projectArg: Project) => {
|
||||||
// lets care about tsconfig.json
|
// lets care about tsconfig.json
|
||||||
const tsconfigSmartfile = await plugins.smartfile.SmartFile.fromFilePath(plugins.path.join(paths.cwd, 'tsconfig.json'));
|
logger.log('info', 'Formatting tsconfig.json...');
|
||||||
|
const tsconfigSmartfile = await plugins.smartfile.SmartFile.fromFilePath(
|
||||||
|
plugins.path.join(paths.cwd, 'tsconfig.json'),
|
||||||
|
);
|
||||||
const tsconfigObject = JSON.parse(tsconfigSmartfile.contentBuffer.toString());
|
const tsconfigObject = JSON.parse(tsconfigSmartfile.contentBuffer.toString());
|
||||||
tsconfigObject.compilerOptions = tsconfigObject.compilerOptions || {};
|
tsconfigObject.compilerOptions = tsconfigObject.compilerOptions || {};
|
||||||
tsconfigObject.compilerOptions.baseUrl = '.';
|
tsconfigObject.compilerOptions.baseUrl = '.';
|
||||||
@@ -16,8 +19,12 @@ export const run = async (projectArg: Project) => {
|
|||||||
const publishModules = await tsPublishInstance.getModuleSubDirs(paths.cwd);
|
const publishModules = await tsPublishInstance.getModuleSubDirs(paths.cwd);
|
||||||
for (const publishModule of Object.keys(publishModules)) {
|
for (const publishModule of Object.keys(publishModules)) {
|
||||||
const publishConfig = publishModules[publishModule];
|
const publishConfig = publishModules[publishModule];
|
||||||
tsconfigObject.compilerOptions.paths[`${publishConfig.name}`] = [`./${publishModule}/index.ts`];
|
tsconfigObject.compilerOptions.paths[`${publishConfig.name}`] = [
|
||||||
|
`./${publishModule}/index.js`,
|
||||||
|
];
|
||||||
}
|
}
|
||||||
tsconfigSmartfile.setContentsFromString(JSON.stringify(tsconfigObject, null, 2));
|
tsconfigSmartfile.setContentsFromString(
|
||||||
|
JSON.stringify(tsconfigObject, null, 2),
|
||||||
|
);
|
||||||
await tsconfigSmartfile.write();
|
await tsconfigSmartfile.write();
|
||||||
};
|
};
|
44
ts/mod_format/formatters/cleanup.formatter.ts
Normal file
44
ts/mod_format/formatters/cleanup.formatter.ts
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import { BaseFormatter } from '../classes.baseformatter.js';
|
||||||
|
import type { IPlannedChange } from '../interfaces.format.js';
|
||||||
|
import * as plugins from '../mod.plugins.js';
|
||||||
|
import * as cleanupFormatter from '../format.cleanup.js';
|
||||||
|
|
||||||
|
export class CleanupFormatter extends BaseFormatter {
|
||||||
|
get name(): string {
|
||||||
|
return 'cleanup';
|
||||||
|
}
|
||||||
|
|
||||||
|
async analyze(): Promise<IPlannedChange[]> {
|
||||||
|
const changes: IPlannedChange[] = [];
|
||||||
|
|
||||||
|
// List of files to remove
|
||||||
|
const filesToRemove = [
|
||||||
|
'yarn.lock',
|
||||||
|
'package-lock.json',
|
||||||
|
'tslint.json',
|
||||||
|
'defaults.yml',
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const file of filesToRemove) {
|
||||||
|
const exists = await plugins.smartfile.fs.fileExists(file);
|
||||||
|
if (exists) {
|
||||||
|
changes.push({
|
||||||
|
type: 'delete',
|
||||||
|
path: file,
|
||||||
|
module: this.name,
|
||||||
|
description: `Remove obsolete file`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return changes;
|
||||||
|
}
|
||||||
|
|
||||||
|
async applyChange(change: IPlannedChange): Promise<void> {
|
||||||
|
switch (change.type) {
|
||||||
|
case 'delete':
|
||||||
|
await this.deleteFile(change.path);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
8
ts/mod_format/formatters/copy.formatter.ts
Normal file
8
ts/mod_format/formatters/copy.formatter.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import { LegacyFormatter } from './legacy.formatter.js';
|
||||||
|
import * as formatCopy from '../format.copy.js';
|
||||||
|
|
||||||
|
export class CopyFormatter extends LegacyFormatter {
|
||||||
|
constructor(context: any, project: any) {
|
||||||
|
super(context, project, 'copy', formatCopy);
|
||||||
|
}
|
||||||
|
}
|
8
ts/mod_format/formatters/gitignore.formatter.ts
Normal file
8
ts/mod_format/formatters/gitignore.formatter.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import { LegacyFormatter } from './legacy.formatter.js';
|
||||||
|
import * as formatGitignore from '../format.gitignore.js';
|
||||||
|
|
||||||
|
export class GitignoreFormatter extends LegacyFormatter {
|
||||||
|
constructor(context: any, project: any) {
|
||||||
|
super(context, project, 'gitignore', formatGitignore);
|
||||||
|
}
|
||||||
|
}
|
43
ts/mod_format/formatters/legacy.formatter.ts
Normal file
43
ts/mod_format/formatters/legacy.formatter.ts
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import { BaseFormatter } from '../classes.baseformatter.js';
|
||||||
|
import type { IPlannedChange } from '../interfaces.format.js';
|
||||||
|
import { Project } from '../../classes.project.js';
|
||||||
|
import * as plugins from '../mod.plugins.js';
|
||||||
|
|
||||||
|
// This is a wrapper for existing format modules
|
||||||
|
export class LegacyFormatter extends BaseFormatter {
|
||||||
|
private moduleName: string;
|
||||||
|
private formatModule: any;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
context: any,
|
||||||
|
project: Project,
|
||||||
|
moduleName: string,
|
||||||
|
formatModule: any,
|
||||||
|
) {
|
||||||
|
super(context, project);
|
||||||
|
this.moduleName = moduleName;
|
||||||
|
this.formatModule = formatModule;
|
||||||
|
}
|
||||||
|
|
||||||
|
get name(): string {
|
||||||
|
return this.moduleName;
|
||||||
|
}
|
||||||
|
|
||||||
|
async analyze(): Promise<IPlannedChange[]> {
|
||||||
|
// For legacy modules, we can't easily predict changes
|
||||||
|
// So we'll return a generic change that indicates the module will run
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
type: 'modify',
|
||||||
|
path: '<various files>',
|
||||||
|
module: this.name,
|
||||||
|
description: `Run ${this.name} formatter`,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
async applyChange(change: IPlannedChange): Promise<void> {
|
||||||
|
// Run the legacy format module
|
||||||
|
await this.formatModule.run(this.project);
|
||||||
|
}
|
||||||
|
}
|
8
ts/mod_format/formatters/license.formatter.ts
Normal file
8
ts/mod_format/formatters/license.formatter.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import { LegacyFormatter } from './legacy.formatter.js';
|
||||||
|
import * as formatLicense from '../format.license.js';
|
||||||
|
|
||||||
|
export class LicenseFormatter extends LegacyFormatter {
|
||||||
|
constructor(context: any, project: any) {
|
||||||
|
super(context, project, 'license', formatLicense);
|
||||||
|
}
|
||||||
|
}
|
8
ts/mod_format/formatters/npmextra.formatter.ts
Normal file
8
ts/mod_format/formatters/npmextra.formatter.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import { LegacyFormatter } from './legacy.formatter.js';
|
||||||
|
import * as formatNpmextra from '../format.npmextra.js';
|
||||||
|
|
||||||
|
export class NpmextraFormatter extends LegacyFormatter {
|
||||||
|
constructor(context: any, project: any) {
|
||||||
|
super(context, project, 'npmextra', formatNpmextra);
|
||||||
|
}
|
||||||
|
}
|
8
ts/mod_format/formatters/packagejson.formatter.ts
Normal file
8
ts/mod_format/formatters/packagejson.formatter.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import { LegacyFormatter } from './legacy.formatter.js';
|
||||||
|
import * as formatPackageJson from '../format.packagejson.js';
|
||||||
|
|
||||||
|
export class PackageJsonFormatter extends LegacyFormatter {
|
||||||
|
constructor(context: any, project: any) {
|
||||||
|
super(context, project, 'packagejson', formatPackageJson);
|
||||||
|
}
|
||||||
|
}
|
190
ts/mod_format/formatters/prettier.formatter.ts
Normal file
190
ts/mod_format/formatters/prettier.formatter.ts
Normal file
@@ -0,0 +1,190 @@
|
|||||||
|
import { BaseFormatter } from '../classes.baseformatter.js';
|
||||||
|
import type { IPlannedChange } from '../interfaces.format.js';
|
||||||
|
import * as plugins from '../mod.plugins.js';
|
||||||
|
import { logger, logVerbose } from '../../gitzone.logging.js';
|
||||||
|
|
||||||
|
export class PrettierFormatter extends BaseFormatter {
|
||||||
|
get name(): string {
|
||||||
|
return 'prettier';
|
||||||
|
}
|
||||||
|
|
||||||
|
async analyze(): Promise<IPlannedChange[]> {
|
||||||
|
const changes: IPlannedChange[] = [];
|
||||||
|
|
||||||
|
// Define directories to format (TypeScript directories by default)
|
||||||
|
const includeDirs = ['ts', 'ts_*', 'test', 'tests'];
|
||||||
|
|
||||||
|
// File extensions to format
|
||||||
|
const extensions = '{ts,tsx,js,jsx,json,md,css,scss,html,xml,yaml,yml}';
|
||||||
|
|
||||||
|
// Also format root-level config files
|
||||||
|
const rootConfigFiles = [
|
||||||
|
'package.json',
|
||||||
|
'tsconfig.json',
|
||||||
|
'npmextra.json',
|
||||||
|
'.prettierrc',
|
||||||
|
'.prettierrc.json',
|
||||||
|
'.prettierrc.js',
|
||||||
|
'readme.md',
|
||||||
|
'README.md',
|
||||||
|
'changelog.md',
|
||||||
|
'CHANGELOG.md',
|
||||||
|
'license',
|
||||||
|
'LICENSE',
|
||||||
|
'*.md',
|
||||||
|
];
|
||||||
|
|
||||||
|
// Collect all files to format
|
||||||
|
const allFiles: string[] = [];
|
||||||
|
|
||||||
|
// Add files from TypeScript directories
|
||||||
|
for (const dir of includeDirs) {
|
||||||
|
const globPattern = `${dir}/**/*.${extensions}`;
|
||||||
|
const dirFiles = await plugins.smartfile.fs.listFileTree(
|
||||||
|
'.',
|
||||||
|
globPattern,
|
||||||
|
);
|
||||||
|
allFiles.push(...dirFiles);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add root config files
|
||||||
|
for (const pattern of rootConfigFiles) {
|
||||||
|
const rootFiles = await plugins.smartfile.fs.listFileTree('.', pattern);
|
||||||
|
// Only include files at root level (no slashes in path)
|
||||||
|
const rootLevelFiles = rootFiles.filter((f) => !f.includes('/'));
|
||||||
|
allFiles.push(...rootLevelFiles);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove duplicates
|
||||||
|
const uniqueFiles = [...new Set(allFiles)];
|
||||||
|
|
||||||
|
// Get all files that match the pattern
|
||||||
|
const files = uniqueFiles;
|
||||||
|
|
||||||
|
// Ensure we only process actual files (not directories)
|
||||||
|
const validFiles: string[] = [];
|
||||||
|
for (const file of files) {
|
||||||
|
try {
|
||||||
|
const stats = await plugins.smartfile.fs.stat(file);
|
||||||
|
if (!stats.isDirectory()) {
|
||||||
|
validFiles.push(file);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// Skip files that can't be accessed
|
||||||
|
logVerbose(`Skipping ${file} - cannot access: ${error.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check which files need formatting
|
||||||
|
for (const file of validFiles) {
|
||||||
|
// Skip files that haven't changed
|
||||||
|
if (!(await this.shouldProcessFile(file))) {
|
||||||
|
logVerbose(`Skipping ${file} - no changes detected`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
changes.push({
|
||||||
|
type: 'modify',
|
||||||
|
path: file,
|
||||||
|
module: this.name,
|
||||||
|
description: 'Format with Prettier',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.log('info', `Found ${changes.length} files to format with Prettier`);
|
||||||
|
return changes;
|
||||||
|
}
|
||||||
|
|
||||||
|
async execute(changes: IPlannedChange[]): Promise<void> {
|
||||||
|
const startTime = this.stats.moduleStartTime(this.name);
|
||||||
|
this.stats.startModule(this.name);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.preExecute();
|
||||||
|
|
||||||
|
// Batch process files
|
||||||
|
const batchSize = 10; // Process 10 files at a time
|
||||||
|
const batches: IPlannedChange[][] = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < changes.length; i += batchSize) {
|
||||||
|
batches.push(changes.slice(i, i + batchSize));
|
||||||
|
}
|
||||||
|
|
||||||
|
logVerbose(
|
||||||
|
`Processing ${changes.length} files in ${batches.length} batches`,
|
||||||
|
);
|
||||||
|
|
||||||
|
for (let i = 0; i < batches.length; i++) {
|
||||||
|
const batch = batches[i];
|
||||||
|
logVerbose(
|
||||||
|
`Processing batch ${i + 1}/${batches.length} (${batch.length} files)`,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Process batch in parallel
|
||||||
|
const promises = batch.map(async (change) => {
|
||||||
|
try {
|
||||||
|
await this.applyChange(change);
|
||||||
|
this.stats.recordFileOperation(this.name, change.type, true);
|
||||||
|
} catch (error) {
|
||||||
|
this.stats.recordFileOperation(this.name, change.type, false);
|
||||||
|
logger.log(
|
||||||
|
'error',
|
||||||
|
`Failed to format ${change.path}: ${error.message}`,
|
||||||
|
);
|
||||||
|
// Don't throw - continue with other files
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await Promise.all(promises);
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.postExecute();
|
||||||
|
} catch (error) {
|
||||||
|
// Rollback removed - no longer tracking operations
|
||||||
|
throw error;
|
||||||
|
} finally {
|
||||||
|
this.stats.endModule(this.name, startTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async applyChange(change: IPlannedChange): Promise<void> {
|
||||||
|
if (change.type !== 'modify') return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Read current content
|
||||||
|
const content = plugins.smartfile.fs.toStringSync(change.path);
|
||||||
|
|
||||||
|
// Format with prettier
|
||||||
|
const prettier = await import('prettier');
|
||||||
|
const formatted = await prettier.format(content, {
|
||||||
|
filepath: change.path,
|
||||||
|
...(await this.getPrettierConfig()),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Only write if content actually changed
|
||||||
|
if (formatted !== content) {
|
||||||
|
await this.modifyFile(change.path, formatted);
|
||||||
|
logVerbose(`Formatted ${change.path}`);
|
||||||
|
} else {
|
||||||
|
logVerbose(`No formatting changes for ${change.path}`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
logger.log('error', `Failed to format ${change.path}: ${error.message}`);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getPrettierConfig(): Promise<any> {
|
||||||
|
// Try to load prettier config from the project
|
||||||
|
const prettierConfig = new plugins.npmextra.Npmextra();
|
||||||
|
return prettierConfig.dataFor('prettier', {
|
||||||
|
// Default prettier config
|
||||||
|
singleQuote: true,
|
||||||
|
trailingComma: 'all',
|
||||||
|
printWidth: 80,
|
||||||
|
tabWidth: 2,
|
||||||
|
semi: true,
|
||||||
|
arrowParens: 'always',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
24
ts/mod_format/formatters/readme.formatter.ts
Normal file
24
ts/mod_format/formatters/readme.formatter.ts
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import { BaseFormatter } from '../classes.baseformatter.js';
|
||||||
|
import type { IPlannedChange } from '../interfaces.format.js';
|
||||||
|
import * as formatReadme from '../format.readme.js';
|
||||||
|
|
||||||
|
export class ReadmeFormatter extends BaseFormatter {
|
||||||
|
get name(): string {
|
||||||
|
return 'readme';
|
||||||
|
}
|
||||||
|
|
||||||
|
async analyze(): Promise<IPlannedChange[]> {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
type: 'modify',
|
||||||
|
path: 'readme.md',
|
||||||
|
module: this.name,
|
||||||
|
description: 'Ensure readme files exist',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
async applyChange(change: IPlannedChange): Promise<void> {
|
||||||
|
await formatReadme.run();
|
||||||
|
}
|
||||||
|
}
|
8
ts/mod_format/formatters/templates.formatter.ts
Normal file
8
ts/mod_format/formatters/templates.formatter.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import { LegacyFormatter } from './legacy.formatter.js';
|
||||||
|
import * as formatTemplates from '../format.templates.js';
|
||||||
|
|
||||||
|
export class TemplatesFormatter extends LegacyFormatter {
|
||||||
|
constructor(context: any, project: any) {
|
||||||
|
super(context, project, 'templates', formatTemplates);
|
||||||
|
}
|
||||||
|
}
|
8
ts/mod_format/formatters/tsconfig.formatter.ts
Normal file
8
ts/mod_format/formatters/tsconfig.formatter.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import { LegacyFormatter } from './legacy.formatter.js';
|
||||||
|
import * as formatTsconfig from '../format.tsconfig.js';
|
||||||
|
|
||||||
|
export class TsconfigFormatter extends LegacyFormatter {
|
||||||
|
constructor(context: any, project: any) {
|
||||||
|
super(context, project, 'tsconfig', formatTsconfig);
|
||||||
|
}
|
||||||
|
}
|
@@ -1,40 +1,189 @@
|
|||||||
import * as plugins from './mod.plugins.js';
|
import * as plugins from './mod.plugins.js';
|
||||||
import { Project } from '../classes.project.js';
|
import { Project } from '../classes.project.js';
|
||||||
|
import { FormatContext } from './classes.formatcontext.js';
|
||||||
|
import { FormatPlanner } from './classes.formatplanner.js';
|
||||||
|
import { logger, setVerboseMode } from '../gitzone.logging.js';
|
||||||
|
|
||||||
|
// Import wrapper classes for formatters
|
||||||
|
import { CleanupFormatter } from './formatters/cleanup.formatter.js';
|
||||||
|
import { NpmextraFormatter } from './formatters/npmextra.formatter.js';
|
||||||
|
import { LicenseFormatter } from './formatters/license.formatter.js';
|
||||||
|
import { PackageJsonFormatter } from './formatters/packagejson.formatter.js';
|
||||||
|
import { TemplatesFormatter } from './formatters/templates.formatter.js';
|
||||||
|
import { GitignoreFormatter } from './formatters/gitignore.formatter.js';
|
||||||
|
import { TsconfigFormatter } from './formatters/tsconfig.formatter.js';
|
||||||
|
import { PrettierFormatter } from './formatters/prettier.formatter.js';
|
||||||
|
import { ReadmeFormatter } from './formatters/readme.formatter.js';
|
||||||
|
import { CopyFormatter } from './formatters/copy.formatter.js';
|
||||||
|
|
||||||
|
export let run = async (
|
||||||
|
options: {
|
||||||
|
dryRun?: boolean;
|
||||||
|
yes?: boolean;
|
||||||
|
planOnly?: boolean;
|
||||||
|
savePlan?: string;
|
||||||
|
fromPlan?: string;
|
||||||
|
detailed?: boolean;
|
||||||
|
interactive?: boolean;
|
||||||
|
parallel?: boolean;
|
||||||
|
verbose?: boolean;
|
||||||
|
} = {},
|
||||||
|
): Promise<any> => {
|
||||||
|
// Set verbose mode if requested
|
||||||
|
if (options.verbose) {
|
||||||
|
setVerboseMode(true);
|
||||||
|
}
|
||||||
|
|
||||||
export let run = async (writeArg: boolean = true): Promise<any> => {
|
|
||||||
const project = await Project.fromCwd();
|
const project = await Project.fromCwd();
|
||||||
|
const context = new FormatContext();
|
||||||
|
// Cache system removed - no longer needed
|
||||||
|
const planner = new FormatPlanner();
|
||||||
|
|
||||||
// cleanup
|
// Get configuration from npmextra
|
||||||
const formatCleanup = await import('./format.cleanup.js');
|
const npmextraConfig = new plugins.npmextra.Npmextra();
|
||||||
await formatCleanup.run(project);
|
const formatConfig = npmextraConfig.dataFor<any>('gitzone.format', {
|
||||||
|
interactive: true,
|
||||||
|
showDiffs: false,
|
||||||
|
autoApprove: false,
|
||||||
|
planTimeout: 30000,
|
||||||
|
rollback: {
|
||||||
|
enabled: true,
|
||||||
|
autoRollbackOnError: true,
|
||||||
|
backupRetentionDays: 7,
|
||||||
|
maxBackupSize: '100MB',
|
||||||
|
excludePatterns: ['node_modules/**', '.git/**'],
|
||||||
|
},
|
||||||
|
modules: {
|
||||||
|
skip: [],
|
||||||
|
only: [],
|
||||||
|
order: [],
|
||||||
|
},
|
||||||
|
parallel: true,
|
||||||
|
cache: {
|
||||||
|
enabled: true,
|
||||||
|
clean: true, // Clean invalid entries from cache
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
// npmextra
|
// Cache cleaning removed - no longer using cache system
|
||||||
const formatNpmextra = await import('./format.npmextra.js');
|
|
||||||
await formatNpmextra.run(project);
|
|
||||||
|
|
||||||
// license
|
// Override config with command options
|
||||||
const formatLicense = await import('./format.license.js');
|
const interactive = options.interactive ?? formatConfig.interactive;
|
||||||
await formatLicense.run(project);
|
const autoApprove = options.yes ?? formatConfig.autoApprove;
|
||||||
|
const parallel = options.parallel ?? formatConfig.parallel;
|
||||||
|
|
||||||
// format package.json
|
try {
|
||||||
const formatPackageJson = await import('./format.packagejson.js');
|
// Initialize formatters
|
||||||
await formatPackageJson.run(project);
|
const formatters = [
|
||||||
|
new CleanupFormatter(context, project),
|
||||||
|
new NpmextraFormatter(context, project),
|
||||||
|
new LicenseFormatter(context, project),
|
||||||
|
new PackageJsonFormatter(context, project),
|
||||||
|
new TemplatesFormatter(context, project),
|
||||||
|
new GitignoreFormatter(context, project),
|
||||||
|
new TsconfigFormatter(context, project),
|
||||||
|
new PrettierFormatter(context, project),
|
||||||
|
new ReadmeFormatter(context, project),
|
||||||
|
new CopyFormatter(context, project),
|
||||||
|
];
|
||||||
|
|
||||||
// format .gitlab-ci.yml
|
// Filter formatters based on configuration
|
||||||
const formatTemplates = await import('./format.templates.js');
|
const activeFormatters = formatters.filter((formatter) => {
|
||||||
await formatTemplates.run(project);
|
if (formatConfig.modules.only.length > 0) {
|
||||||
|
return formatConfig.modules.only.includes(formatter.name);
|
||||||
|
}
|
||||||
|
if (formatConfig.modules.skip.includes(formatter.name)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
// format .gitignore
|
// Plan phase
|
||||||
const formatGitignore = await import('./format.gitignore.js');
|
logger.log('info', 'Analyzing project for format operations...');
|
||||||
await formatGitignore.run(project);
|
let plan = options.fromPlan
|
||||||
|
? JSON.parse(await plugins.smartfile.fs.toStringSync(options.fromPlan))
|
||||||
|
: await planner.planFormat(activeFormatters);
|
||||||
|
|
||||||
// format TypeScript
|
// Display plan
|
||||||
const formatPrettier = await import('./format.prettier.js');
|
await planner.displayPlan(plan, options.detailed);
|
||||||
await formatPrettier.run(project);
|
|
||||||
const formatTsConfig = await import('./format.tsconfig.js');
|
|
||||||
await formatTsConfig.run(project);
|
|
||||||
|
|
||||||
// format readme.md
|
// Save plan if requested
|
||||||
const formatReadme = await import('./format.readme.js');
|
if (options.savePlan) {
|
||||||
await formatReadme.run();
|
await plugins.smartfile.memory.toFs(
|
||||||
|
JSON.stringify(plan, null, 2),
|
||||||
|
options.savePlan,
|
||||||
|
);
|
||||||
|
logger.log('info', `Plan saved to ${options.savePlan}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exit if plan-only mode
|
||||||
|
if (options.planOnly) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dry-run mode
|
||||||
|
if (options.dryRun) {
|
||||||
|
logger.log('info', 'Dry-run mode - no changes will be made');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interactive confirmation
|
||||||
|
if (interactive && !autoApprove) {
|
||||||
|
const interactInstance = new plugins.smartinteract.SmartInteract();
|
||||||
|
const response = await interactInstance.askQuestion({
|
||||||
|
type: 'confirm',
|
||||||
|
name: 'proceed',
|
||||||
|
message: 'Proceed with formatting?',
|
||||||
|
default: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!(response as any).value) {
|
||||||
|
logger.log('info', 'Format operation cancelled by user');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute phase
|
||||||
|
logger.log(
|
||||||
|
'info',
|
||||||
|
`Executing format operations${parallel ? ' in parallel' : ' sequentially'}...`,
|
||||||
|
);
|
||||||
|
await planner.executePlan(plan, activeFormatters, context, parallel);
|
||||||
|
|
||||||
|
// Finish statistics tracking
|
||||||
|
context.getFormatStats().finish();
|
||||||
|
|
||||||
|
// Display statistics
|
||||||
|
const showStats = npmextraConfig.dataFor('gitzone.format.showStats', true);
|
||||||
|
if (showStats) {
|
||||||
|
context.getFormatStats().displayStats();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save stats if requested
|
||||||
|
if (options.detailed) {
|
||||||
|
const statsPath = `.nogit/format-stats-${Date.now()}.json`;
|
||||||
|
await context.getFormatStats().saveReport(statsPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.log('success', 'Format operations completed successfully!');
|
||||||
|
} catch (error) {
|
||||||
|
logger.log('error', `Format operation failed: ${error.message}`);
|
||||||
|
|
||||||
|
// Rollback system has been removed for stability
|
||||||
|
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Export CLI command handlers
|
||||||
|
export const handleRollback = async (operationId?: string): Promise<void> => {
|
||||||
|
logger.log('info', 'Rollback system has been disabled for stability');
|
||||||
|
};
|
||||||
|
|
||||||
|
export const handleListBackups = async (): Promise<void> => {
|
||||||
|
logger.log('info', 'Backup system has been disabled for stability');
|
||||||
|
};
|
||||||
|
|
||||||
|
export const handleCleanBackups = async (): Promise<void> => {
|
||||||
|
logger.log('info', 'Backup cleaning has been disabled - backup system removed');
|
||||||
};
|
};
|
||||||
|
45
ts/mod_format/interfaces.format.ts
Normal file
45
ts/mod_format/interfaces.format.ts
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
export type IFormatOperation = {
|
||||||
|
id: string;
|
||||||
|
timestamp: number;
|
||||||
|
files: Array<{
|
||||||
|
path: string;
|
||||||
|
originalContent: string;
|
||||||
|
checksum: string;
|
||||||
|
permissions: string;
|
||||||
|
}>;
|
||||||
|
status: 'pending' | 'in-progress' | 'completed' | 'failed' | 'rolled-back';
|
||||||
|
error?: Error;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type IFormatPlan = {
|
||||||
|
summary: {
|
||||||
|
totalFiles: number;
|
||||||
|
filesAdded: number;
|
||||||
|
filesModified: number;
|
||||||
|
filesRemoved: number;
|
||||||
|
estimatedTime: number;
|
||||||
|
};
|
||||||
|
changes: Array<{
|
||||||
|
type: 'create' | 'modify' | 'delete';
|
||||||
|
path: string;
|
||||||
|
module: string;
|
||||||
|
description: string;
|
||||||
|
diff?: string;
|
||||||
|
size?: number;
|
||||||
|
}>;
|
||||||
|
warnings: Array<{
|
||||||
|
level: 'info' | 'warning' | 'error';
|
||||||
|
message: string;
|
||||||
|
module: string;
|
||||||
|
}>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type IPlannedChange = {
|
||||||
|
type: 'create' | 'modify' | 'delete';
|
||||||
|
path: string;
|
||||||
|
module: string;
|
||||||
|
description: string;
|
||||||
|
content?: string; // For create/modify operations
|
||||||
|
diff?: string;
|
||||||
|
size?: number;
|
||||||
|
};
|
@@ -1,5 +1,7 @@
|
|||||||
export * from '../plugins.js';
|
export * from '../plugins.js';
|
||||||
|
|
||||||
|
import * as crypto from 'crypto';
|
||||||
|
import * as path from 'path';
|
||||||
import * as lik from '@push.rocks/lik';
|
import * as lik from '@push.rocks/lik';
|
||||||
import * as smartfile from '@push.rocks/smartfile';
|
import * as smartfile from '@push.rocks/smartfile';
|
||||||
import * as smartgulp from '@push.rocks/smartgulp';
|
import * as smartgulp from '@push.rocks/smartgulp';
|
||||||
@@ -9,8 +11,12 @@ import * as smartobject from '@push.rocks/smartobject';
|
|||||||
import * as smartnpm from '@push.rocks/smartnpm';
|
import * as smartnpm from '@push.rocks/smartnpm';
|
||||||
import * as smartstream from '@push.rocks/smartstream';
|
import * as smartstream from '@push.rocks/smartstream';
|
||||||
import * as through2 from 'through2';
|
import * as through2 from 'through2';
|
||||||
|
import * as npmextra from '@push.rocks/npmextra';
|
||||||
|
import * as smartdiff from '@push.rocks/smartdiff';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
crypto,
|
||||||
|
path,
|
||||||
lik,
|
lik,
|
||||||
smartfile,
|
smartfile,
|
||||||
smartgulp,
|
smartgulp,
|
||||||
@@ -20,4 +26,6 @@ export {
|
|||||||
smartnpm,
|
smartnpm,
|
||||||
smartstream,
|
smartstream,
|
||||||
through2,
|
through2,
|
||||||
|
npmextra,
|
||||||
|
smartdiff,
|
||||||
};
|
};
|
||||||
|
@@ -35,7 +35,10 @@ export class Meta {
|
|||||||
* sorts the metaRepoData
|
* sorts the metaRepoData
|
||||||
*/
|
*/
|
||||||
public async sortMetaRepoData() {
|
public async sortMetaRepoData() {
|
||||||
const stringifiedMetadata = plugins.smartjson.stringify(this.metaRepoData, []);
|
const stringifiedMetadata = plugins.smartjson.stringify(
|
||||||
|
this.metaRepoData,
|
||||||
|
[],
|
||||||
|
);
|
||||||
this.metaRepoData = plugins.smartjson.parse(stringifiedMetadata);
|
this.metaRepoData = plugins.smartjson.parse(stringifiedMetadata);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,11 +48,15 @@ export class Meta {
|
|||||||
public async readDirectory() {
|
public async readDirectory() {
|
||||||
await this.syncToRemote(true);
|
await this.syncToRemote(true);
|
||||||
logger.log('info', `reading directory`);
|
logger.log('info', `reading directory`);
|
||||||
const metaFileExists = plugins.smartfile.fs.fileExistsSync(this.filePaths.metaJson);
|
const metaFileExists = plugins.smartfile.fs.fileExistsSync(
|
||||||
|
this.filePaths.metaJson,
|
||||||
|
);
|
||||||
if (!metaFileExists) {
|
if (!metaFileExists) {
|
||||||
throw new Error(`meta file does not exist at ${this.filePaths.metaJson}`);
|
throw new Error(`meta file does not exist at ${this.filePaths.metaJson}`);
|
||||||
}
|
}
|
||||||
this.metaRepoData = plugins.smartfile.fs.toObjectSync(this.filePaths.metaJson);
|
this.metaRepoData = plugins.smartfile.fs.toObjectSync(
|
||||||
|
this.filePaths.metaJson,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -76,7 +83,10 @@ export class Meta {
|
|||||||
this.filePaths.metaJson,
|
this.filePaths.metaJson,
|
||||||
);
|
);
|
||||||
// write .gitignore to disk
|
// write .gitignore to disk
|
||||||
plugins.smartfile.memory.toFsSync(await this.generateGitignore(), this.filePaths.gitIgnore);
|
plugins.smartfile.memory.toFsSync(
|
||||||
|
await this.generateGitignore(),
|
||||||
|
this.filePaths.gitIgnore,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -84,13 +94,17 @@ export class Meta {
|
|||||||
*/
|
*/
|
||||||
public async syncToRemote(gitCleanArg = false) {
|
public async syncToRemote(gitCleanArg = false) {
|
||||||
logger.log('info', `syncing from origin master`);
|
logger.log('info', `syncing from origin master`);
|
||||||
await this.smartshellInstance.exec(`cd ${this.cwd} && git pull origin master`);
|
await this.smartshellInstance.exec(
|
||||||
|
`cd ${this.cwd} && git pull origin master`,
|
||||||
|
);
|
||||||
if (gitCleanArg) {
|
if (gitCleanArg) {
|
||||||
logger.log('info', `cleaning the repository from old directories`);
|
logger.log('info', `cleaning the repository from old directories`);
|
||||||
await this.smartshellInstance.exec(`cd ${this.cwd} && git clean -fd`);
|
await this.smartshellInstance.exec(`cd ${this.cwd} && git clean -fd`);
|
||||||
}
|
}
|
||||||
logger.log('info', `syncing to remote origin master`);
|
logger.log('info', `syncing to remote origin master`);
|
||||||
await this.smartshellInstance.exec(`cd ${this.cwd} && git push origin master`);
|
await this.smartshellInstance.exec(
|
||||||
|
`cd ${this.cwd} && git push origin master`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -98,7 +112,9 @@ export class Meta {
|
|||||||
*/
|
*/
|
||||||
public async updateLocalRepos() {
|
public async updateLocalRepos() {
|
||||||
await this.syncToRemote();
|
await this.syncToRemote();
|
||||||
const projects = plugins.smartfile.fs.toObjectSync(this.filePaths.metaJson).projects;
|
const projects = plugins.smartfile.fs.toObjectSync(
|
||||||
|
this.filePaths.metaJson,
|
||||||
|
).projects;
|
||||||
const preExistingFolders = plugins.smartfile.fs.listFoldersSync(this.cwd);
|
const preExistingFolders = plugins.smartfile.fs.listFoldersSync(this.cwd);
|
||||||
for (const preExistingFolderArg of preExistingFolders) {
|
for (const preExistingFolderArg of preExistingFolders) {
|
||||||
if (
|
if (
|
||||||
@@ -107,14 +123,18 @@ export class Meta {
|
|||||||
projectFolder.startsWith(preExistingFolderArg),
|
projectFolder.startsWith(preExistingFolderArg),
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
const response = await plugins.smartinteraction.SmartInteract.getCliConfirmation(
|
const response =
|
||||||
`Do you want to delete superfluous directory >>${preExistingFolderArg}<< ?`,
|
await plugins.smartinteraction.SmartInteract.getCliConfirmation(
|
||||||
true,
|
`Do you want to delete superfluous directory >>${preExistingFolderArg}<< ?`,
|
||||||
);
|
true,
|
||||||
|
);
|
||||||
if (response) {
|
if (response) {
|
||||||
logger.log('warn', `Deleting >>${preExistingFolderArg}<<!`);
|
logger.log('warn', `Deleting >>${preExistingFolderArg}<<!`);
|
||||||
} else {
|
} else {
|
||||||
logger.log('warn', `Not deleting ${preExistingFolderArg} by request!`);
|
logger.log(
|
||||||
|
'warn',
|
||||||
|
`Not deleting ${preExistingFolderArg} by request!`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -160,7 +180,9 @@ export class Meta {
|
|||||||
*/
|
*/
|
||||||
public async initProject() {
|
public async initProject() {
|
||||||
await this.syncToRemote(true);
|
await this.syncToRemote(true);
|
||||||
const fileExists = await plugins.smartfile.fs.fileExists(this.filePaths.metaJson);
|
const fileExists = await plugins.smartfile.fs.fileExists(
|
||||||
|
this.filePaths.metaJson,
|
||||||
|
);
|
||||||
if (!fileExists) {
|
if (!fileExists) {
|
||||||
await plugins.smartfile.memory.toFs(
|
await plugins.smartfile.memory.toFs(
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
@@ -168,7 +190,10 @@ export class Meta {
|
|||||||
}),
|
}),
|
||||||
this.filePaths.metaJson,
|
this.filePaths.metaJson,
|
||||||
);
|
);
|
||||||
logger.log(`success`, `created a new .meta.json in directory ${this.cwd}`);
|
logger.log(
|
||||||
|
`success`,
|
||||||
|
`created a new .meta.json in directory ${this.cwd}`,
|
||||||
|
);
|
||||||
await plugins.smartfile.memory.toFs(
|
await plugins.smartfile.memory.toFs(
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
name: this.dirName,
|
name: this.dirName,
|
||||||
@@ -176,9 +201,15 @@ export class Meta {
|
|||||||
}),
|
}),
|
||||||
this.filePaths.packageJson,
|
this.filePaths.packageJson,
|
||||||
);
|
);
|
||||||
logger.log(`success`, `created a new package.json in directory ${this.cwd}`);
|
logger.log(
|
||||||
|
`success`,
|
||||||
|
`created a new package.json in directory ${this.cwd}`,
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
logger.log(`error`, `directory ${this.cwd} already has a .metaJson file. Doing nothing.`);
|
logger.log(
|
||||||
|
`error`,
|
||||||
|
`directory ${this.cwd} already has a .metaJson file. Doing nothing.`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
await this.smartshellInstance.exec(
|
await this.smartshellInstance.exec(
|
||||||
`cd ${this.cwd} && git add -A && git commit -m "feat(project): init meta project for ${this.dirName}"`,
|
`cd ${this.cwd} && git add -A && git commit -m "feat(project): init meta project for ${this.dirName}"`,
|
||||||
@@ -195,7 +226,9 @@ export class Meta {
|
|||||||
const existingProject = this.metaRepoData.projects[projectNameArg];
|
const existingProject = this.metaRepoData.projects[projectNameArg];
|
||||||
|
|
||||||
if (existingProject) {
|
if (existingProject) {
|
||||||
throw new Error('Project already exists! Please remove it first before adding it again.');
|
throw new Error(
|
||||||
|
'Project already exists! Please remove it first before adding it again.',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.metaRepoData.projects[projectNameArg] = gitUrlArg;
|
this.metaRepoData.projects[projectNameArg] = gitUrlArg;
|
||||||
@@ -217,7 +250,10 @@ export class Meta {
|
|||||||
const existingProject = this.metaRepoData.projects[projectNameArg];
|
const existingProject = this.metaRepoData.projects[projectNameArg];
|
||||||
|
|
||||||
if (!existingProject) {
|
if (!existingProject) {
|
||||||
logger.log('error', `Project ${projectNameArg} does not exist! So it cannot be removed`);
|
logger.log(
|
||||||
|
'error',
|
||||||
|
`Project ${projectNameArg} does not exist! So it cannot be removed`,
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -228,7 +264,9 @@ export class Meta {
|
|||||||
await this.writeToDisk();
|
await this.writeToDisk();
|
||||||
|
|
||||||
logger.log('info', 'removing directory from cwd');
|
logger.log('info', 'removing directory from cwd');
|
||||||
await plugins.smartfile.fs.remove(plugins.path.join(paths.cwd, projectNameArg));
|
await plugins.smartfile.fs.remove(
|
||||||
|
plugins.path.join(paths.cwd, projectNameArg),
|
||||||
|
);
|
||||||
await this.updateLocalRepos();
|
await this.updateLocalRepos();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -16,7 +16,9 @@ export let run = () => {
|
|||||||
* create a new project with 'gitzone template [template]'
|
* create a new project with 'gitzone template [template]'
|
||||||
the following templates exist: ${(() => {
|
the following templates exist: ${(() => {
|
||||||
let projects = `\n`;
|
let projects = `\n`;
|
||||||
for (const template of plugins.smartfile.fs.listFoldersSync(paths.templatesDir)) {
|
for (const template of plugins.smartfile.fs.listFoldersSync(
|
||||||
|
paths.templatesDir,
|
||||||
|
)) {
|
||||||
projects += ` - ${template}\n`;
|
projects += ` - ${template}\n`;
|
||||||
}
|
}
|
||||||
return projects;
|
return projects;
|
||||||
|
@@ -15,7 +15,9 @@ export const run = async (argvArg: any) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
await smartshellInstance.execStrict(`cd ${paths.cwd} && git checkout master`);
|
await smartshellInstance.execStrict(`cd ${paths.cwd} && git checkout master`);
|
||||||
await smartshellInstance.execStrict(`cd ${paths.cwd} && git pull origin master`);
|
await smartshellInstance.execStrict(
|
||||||
|
`cd ${paths.cwd} && git pull origin master`,
|
||||||
|
);
|
||||||
await smartshellInstance.execStrict(`cd ${paths.cwd} && npm ci`);
|
await smartshellInstance.execStrict(`cd ${paths.cwd} && npm ci`);
|
||||||
|
|
||||||
await provideNoGitFiles();
|
await provideNoGitFiles();
|
||||||
|
@@ -16,7 +16,9 @@ export const isTemplate = async (templateNameArg: string) => {
|
|||||||
|
|
||||||
export const getTemplate = async (templateNameArg: string) => {
|
export const getTemplate = async (templateNameArg: string) => {
|
||||||
if (isTemplate(templateNameArg)) {
|
if (isTemplate(templateNameArg)) {
|
||||||
const localScafTemplate = new plugins.smartscaf.ScafTemplate(getTemplatePath(templateNameArg));
|
const localScafTemplate = new plugins.smartscaf.ScafTemplate(
|
||||||
|
getTemplatePath(templateNameArg),
|
||||||
|
);
|
||||||
await localScafTemplate.readTemplateFromDir();
|
await localScafTemplate.readTemplateFromDir();
|
||||||
return localScafTemplate;
|
return localScafTemplate;
|
||||||
} else {
|
} else {
|
||||||
@@ -32,7 +34,8 @@ export const run = async (argvArg: any) => {
|
|||||||
const answerBucket = await smartinteract.askQuestion({
|
const answerBucket = await smartinteract.askQuestion({
|
||||||
type: 'list',
|
type: 'list',
|
||||||
default: 'npm',
|
default: 'npm',
|
||||||
message: 'What template do you want to scaffold? (Only showing mpost common options)',
|
message:
|
||||||
|
'What template do you want to scaffold? (Only showing mpost common options)',
|
||||||
name: 'templateName',
|
name: 'templateName',
|
||||||
choices: ['npm', 'service', 'wcc', 'website'],
|
choices: ['npm', 'service', 'wcc', 'website'],
|
||||||
});
|
});
|
||||||
|
@@ -6,7 +6,9 @@
|
|||||||
"module": "NodeNext",
|
"module": "NodeNext",
|
||||||
"moduleResolution": "NodeNext",
|
"moduleResolution": "NodeNext",
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"verbatimModuleSyntax": true
|
"verbatimModuleSyntax": true,
|
||||||
|
"baseUrl": ".",
|
||||||
|
"paths": {}
|
||||||
},
|
},
|
||||||
"exclude": [
|
"exclude": [
|
||||||
"dist_*/**/*.d.ts"
|
"dist_*/**/*.d.ts"
|
||||||
|
Reference in New Issue
Block a user