archicat 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +206 -0
- package/bin/archicat.mjs +4 -0
- package/dist/cli/index.mjs +28 -0
- package/dist/src/index.cjs +1 -0
- package/dist/src/index.d.cts +319 -0
- package/dist/src/index.d.mts +319 -0
- package/dist/src/index.mjs +1 -0
- package/package.json +70 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 BuildPlease
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
# ArchiCat
|
|
2
|
+
|
|
3
|
+
**M²: Modular Mirroring.**
|
|
4
|
+
|
|
5
|
+
ArchiCat is a Gradle-like generative architecture framework for TypeScript.
|
|
6
|
+
|
|
7
|
+
TypeScript asks: **will this import resolve?**
|
|
8
|
+
ArchiCat asks: **should this import exist?**
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm i -D archicat
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Core rule
|
|
15
|
+
|
|
16
|
+
```txt
|
|
17
|
+
dependency graph = import permission graph
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
A source may import another ArchiCat target only when the dependency graph allows it.
|
|
21
|
+
|
|
22
|
+
ArchiCat validates:
|
|
23
|
+
|
|
24
|
+
```txt
|
|
25
|
+
- unknown dependencies
|
|
26
|
+
- self dependencies
|
|
27
|
+
- API -> implementation dependencies
|
|
28
|
+
- circular dependencies
|
|
29
|
+
- cross-target source-path imports
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Targets
|
|
33
|
+
|
|
34
|
+
A module or library has two targets:
|
|
35
|
+
|
|
36
|
+
```txt
|
|
37
|
+
module.dummy.api
|
|
38
|
+
module.dummy.impl
|
|
39
|
+
library.sample.api
|
|
40
|
+
library.sample.impl
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
An app is a composition root:
|
|
44
|
+
|
|
45
|
+
```txt
|
|
46
|
+
app.test
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Dependency rules
|
|
50
|
+
|
|
51
|
+
```txt
|
|
52
|
+
api -> api targets only
|
|
53
|
+
impl -> own api + declared targets
|
|
54
|
+
app -> declared api/impl targets
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Implementation targets are not public by default. They are importable only when declared as dependencies.
|
|
58
|
+
|
|
59
|
+
## Module
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
import { defineModule } from 'archicat';
|
|
63
|
+
|
|
64
|
+
export default defineModule({
|
|
65
|
+
name: 'dummy',
|
|
66
|
+
|
|
67
|
+
api: {
|
|
68
|
+
root: './api',
|
|
69
|
+
dependencies: ['module.mock.api'],
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
impl: {
|
|
73
|
+
root: './impl',
|
|
74
|
+
dependencies: ['module.mock.api', 'library.sample.api'],
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Library
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
import { defineLibrary } from 'archicat';
|
|
83
|
+
|
|
84
|
+
export default defineLibrary({
|
|
85
|
+
name: 'sample',
|
|
86
|
+
|
|
87
|
+
api: './api',
|
|
88
|
+
impl: './impl',
|
|
89
|
+
});
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## App
|
|
93
|
+
|
|
94
|
+
```ts
|
|
95
|
+
import { defineApp } from 'archicat';
|
|
96
|
+
|
|
97
|
+
export default defineApp({
|
|
98
|
+
name: 'test',
|
|
99
|
+
root: './src/app',
|
|
100
|
+
|
|
101
|
+
dependencies: ['module.dummy.impl', 'library.sample.impl'],
|
|
102
|
+
});
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Imports
|
|
106
|
+
|
|
107
|
+
Public API:
|
|
108
|
+
|
|
109
|
+
```ts
|
|
110
|
+
import { dummyApi } from '#modules/dummy/api/index.js';
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Declared implementation dependency:
|
|
114
|
+
|
|
115
|
+
```ts
|
|
116
|
+
import { dummyImpl } from '#modules/dummy/impl/index.js';
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Blocked without dependency:
|
|
120
|
+
|
|
121
|
+
```ts
|
|
122
|
+
import { dummyImpl } from '#modules/dummy/impl/index.js';
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Blocked source-path boundary bypass:
|
|
126
|
+
|
|
127
|
+
```ts
|
|
128
|
+
import { dummyImpl } from '../../dummy/impl/index.js';
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Local same-target imports stay normal:
|
|
132
|
+
|
|
133
|
+
```ts
|
|
134
|
+
import { dto } from './dto.js';
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Config
|
|
138
|
+
|
|
139
|
+
```ts
|
|
140
|
+
import { defineArchicatConfig } from 'archicat';
|
|
141
|
+
|
|
142
|
+
export default defineArchicatConfig({
|
|
143
|
+
typescript: {
|
|
144
|
+
tsConfig: {
|
|
145
|
+
extends: '../../tsconfig.node.json',
|
|
146
|
+
include: ['bootstrap.ts', 'src/app', 'src/libraries', 'src/modules', 'types'],
|
|
147
|
+
exclude: ['node_modules', 'dist'],
|
|
148
|
+
},
|
|
149
|
+
},
|
|
150
|
+
|
|
151
|
+
alias: {
|
|
152
|
+
'@app': './src/app/index.ts',
|
|
153
|
+
'@app/*': './src/app/*',
|
|
154
|
+
},
|
|
155
|
+
|
|
156
|
+
modules: {
|
|
157
|
+
include: ['./src/modules'],
|
|
158
|
+
alias: '#modules',
|
|
159
|
+
},
|
|
160
|
+
|
|
161
|
+
libraries: {
|
|
162
|
+
include: ['./src/libraries'],
|
|
163
|
+
alias: '#library',
|
|
164
|
+
},
|
|
165
|
+
|
|
166
|
+
apps: {
|
|
167
|
+
include: ['./src/app'],
|
|
168
|
+
},
|
|
169
|
+
});
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
App `tsconfig.json`:
|
|
173
|
+
|
|
174
|
+
```json
|
|
175
|
+
{
|
|
176
|
+
"extends": "./.archicat/tsconfig.json",
|
|
177
|
+
"compilerOptions": {
|
|
178
|
+
"rootDir": ".",
|
|
179
|
+
"outDir": "./dist"
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
User aliases belong in `archicat.config.ts`, not in `compilerOptions.paths`.
|
|
185
|
+
|
|
186
|
+
## Output
|
|
187
|
+
|
|
188
|
+
```txt
|
|
189
|
+
.archicat/
|
|
190
|
+
tsconfig.json
|
|
191
|
+
modules/
|
|
192
|
+
libraries/
|
|
193
|
+
types/
|
|
194
|
+
reports/
|
|
195
|
+
build.report.json
|
|
196
|
+
graph.report.json
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## Commands
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
archicat generate
|
|
203
|
+
archicat validate
|
|
204
|
+
archicat graph
|
|
205
|
+
archicat doctor
|
|
206
|
+
```
|
package/bin/archicat.mjs
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import{createRequire as e}from"node:module";import{Console as t}from"@buildplease/core/node";import n from"node:fs";import r from"node:path";import i from"typescript";import{createJiti as a}from"jiti";const o={packageName:`archicat`,configFileName:`archicat.config.ts`,root:`.`,outDir:`.archicat`,alias:{},definitions:{moduleFileName:`archicat.module.ts`,libraryFileName:`archicat.library.ts`,appFileName:`archicat.app.ts`},generated:{modulesDirName:`modules`,librariesDirName:`libraries`,typesDirName:`types`,reportsDirName:`reports`,buildReportFileName:`build.report.json`,graphReportFileName:`graph.report.json`,tsconfigFileName:`tsconfig.json`,typesInclude:`./types/**/*.d.ts`,ignoredDirectoryNames:[`node_modules`,`.git`,`.archicat`,`dist`,`build`,`coverage`]},typescript:{consumerTsconfigFileName:`tsconfig.json`,tsConfig:{include:[],exclude:[],files:[]}},modules:{include:[`./src/modules`],alias:`#modules`},libraries:{include:[],alias:`#library`},apps:{include:[]}};function s(e){let t=n.readFileSync(e,`utf8`),r=i.parseConfigFileTextToJson(e,t);if(r.error)throw Error(te(r.error));if(r.config==null||typeof r.config!=`object`||Array.isArray(r.config))throw Error(`Invalid tsconfig object: ${e}`);return r.config}function c(e,t=`tsconfig`){let n=e.compilerOptions;if(n==null)return{};if(typeof n!=`object`||Array.isArray(n))throw Error(`Tsconfig compilerOptions must be an object: ${t}`);return n}function l(e,t){let n=e.extends;if(n===void 0)return[];if(typeof n==`string`)return[d(t,n)];if(Array.isArray(n)&&n.every(e=>typeof e==`string`))return n.map(e=>d(t,e));throw Error(`Tsconfig extends must be a string or string array: ${t}`)}function u(t,n){return r.isAbsolute(n)||n.startsWith(`.`)?ee(r.resolve(t,n),n):f(e(r.join(t,o.configFileName)),n,n)}function d(t,n){return r.isAbsolute(n)||n.startsWith(`.`)?ee(r.resolve(r.dirname(t),n),t):f(e(t),n,t)}function f(e,t,n){for(let n of p(t))try{return e.resolve(n)}catch{continue}throw Error(`Unable to resolve tsconfig extends "${t}": ${n}`)}function ee(e,t){for(let t of p(e))if(n.existsSync(t)&&n.statSync(t).isFile())return t;throw Error(`Unable to resolve tsconfig extends "${e}": ${t}`)}function p(e){return[e,`${e}.json`,r.join(e,o.typescript.consumerTsconfigFileName)]}function te(e){let t=i.flattenDiagnosticMessageText(e.messageText,`
|
|
2
|
+
`);if(e.file&&e.start!==void 0){let n=e.file.getLineAndCharacterOfPosition(e.start);return`${e.file.fileName}:${n.line+1}:${n.character+1} - ${t}`}return t}async function ne(e,t=o.configFileName){let i=r.resolve(e,t);if(!n.existsSync(i))throw Error(`Archicat config was not found: ${i}`);let a=await re(i,e);ue(a,i);let s=ie(a);me(s.modules.alias,s.libraries.alias,i);let c=r.resolve(e,s.root),l=r.resolve(c,s.outDir),u=r.resolve(l,o.generated.reportsDirName),d=le(c,s.typescript.tsConfig.extends);return{configFilePath:i,rootDir:c,outDir:l,reportsDir:u,...d?{tsconfigPath:d}:{},resolvedConfig:s}}async function re(e,t){return await a(t,{interopDefault:!0,extensions:[`.js`,`.cjs`,`.mjs`,`.ts`,`.cts`,`.mts`,`.json`]}).import(e,{default:!0})}function ie(e){return{root:e.root??o.root,outDir:e.outDir??o.outDir,typescript:ae(e),alias:{...o.alias,...e.alias??{}},modules:oe(e.modules),libraries:se(e.libraries),apps:ce(e.apps)}}function ae(e){let t=e.typescript?.tsConfig,n=o.typescript.tsConfig,r={include:[...t?.include??n.include],exclude:[...t?.exclude??n.exclude],files:[...t?.files??n.files]};return t?.extends&&(r.extends=t.extends),{tsConfig:r}}function oe(e){return{include:[...e?.include??o.modules.include],alias:e?.alias??o.modules.alias}}function se(e){return{include:[...e?.include??o.libraries.include],alias:e?.alias??o.libraries.alias}}function ce(e){return{include:[...e?.include??o.apps.include]}}function le(e,t){if(t)return u(e,t)}function ue(e,t){if(typeof e!=`object`||!e)throw Error(`Invalid Archicat config: ${t}`);let n=e;m(n.root,`root`,t),m(n.outDir,`outDir`,t),_(n.typescript,`typescript`,t),_(n.modules,`modules`,t),_(n.libraries,`libraries`,t),_(n.apps,`apps`,t),de(n.typescript?.tsConfig,t),h(n.modules?.include,`modules.include`,t),h(n.libraries?.include,`libraries.include`,t),h(n.apps?.include,`apps.include`,t),g(n.modules?.alias,`modules.alias`,t),g(n.libraries?.alias,`libraries.alias`,t),pe(n.alias,`alias`,t)}function de(e,t){if(e===void 0)return;if(typeof e!=`object`||!e||Array.isArray(e))throw Error(`Archicat config typescript.tsConfig must be an object: ${t}`);let n=e;m(n.extends,`typescript.tsConfig.extends`,t),h(n.include,`typescript.tsConfig.include`,t),h(n.exclude,`typescript.tsConfig.exclude`,t),h(n.files,`typescript.tsConfig.files`,t),fe(n.compilerOptions,t)}function fe(e,t){if(e===void 0)return;if(typeof e!=`object`||!e||Array.isArray(e))throw Error(`Archicat config typescript.tsConfig.compilerOptions must be an object: ${t}`);let n=e;if(Object.hasOwn(n,`paths`))throw Error(`Archicat config typescript.tsConfig.compilerOptions.paths is not supported. Move aliases into archicat.config.ts alias.`);if(Object.hasOwn(n,`baseUrl`))throw Error(`Archicat config typescript.tsConfig.compilerOptions.baseUrl is not supported. Move aliases into archicat.config.ts alias.`);if(Object.keys(n).length>0)throw Error(`Archicat config typescript.tsConfig.compilerOptions is not supported. Put compiler options in the base or app tsconfig.`)}function m(e,t,n){if(e!==void 0&&(typeof e!=`string`||e.trim()===``))throw Error(`Archicat ${t} must be a non-empty string when defined: ${n}`)}function h(e,t,n){if(e!==void 0&&(!Array.isArray(e)||e.some(e=>typeof e!=`string`||e.trim()===``)))throw Error(`Archicat config ${t} must be an array of non-empty strings: ${n}`)}function g(e,t,n){if(e!==void 0&&(typeof e!=`string`||e.trim()===``||e.includes(`*`)||e.endsWith(`/`)))throw Error(`Archicat config ${t} must be a non-empty alias without wildcard or trailing slash: ${n}`)}function pe(e,t,n){if(e!==void 0){if(typeof e!=`object`||!e||Array.isArray(e))throw Error(`Archicat config ${t} must be an object of non-empty string aliases: ${n}`);for(let[r,i]of Object.entries(e))if(r.trim()===``||typeof i!=`string`||i.trim()===``)throw Error(`Archicat config ${t} must contain non-empty string aliases: ${n}`)}}function _(e,t,n){if(e!==void 0&&(typeof e!=`object`||!e||Array.isArray(e)))throw Error(`Archicat config ${t} must be an object: ${n}`)}function me(e,t,n){if(e===t||e.startsWith(`${t}/`)||t.startsWith(`${e}/`))throw Error(`Archicat module and library aliases must use separate roots: "${e}" and "${t}" (${n})`)}function v(e){return e.split(r.sep).join(`/`)}function he(e,t){let n=r.dirname(e),i=r.parse(t),a=r.join(i.dir,i.name),o=v(r.relative(n,a));return o.startsWith(`.`)||(o=`./${o}`),`${o}.js`}function y(e,t){let n=r.relative(t,e);return n===``||!!n&&!n.startsWith(`..`)&&!r.isAbsolute(n)}function ge(e,t){let n=b(e),r=b(t);if(r===n||!y(r,n))throw Error(`Archicat outDir must be a directory inside the project root: ${t}`)}function b(e){let t=r.resolve(e),i=[],a=t;for(;!n.existsSync(a);){let e=r.dirname(a);if(e===a)return t;i.unshift(r.basename(a)),a=e}return r.join(n.realpathSync(a),...i)}function x(e){return e.replace(/\.(?:js|mjs|cjs|ts|mts|cts|tsx)$/u,``)}function S(e,t){return v(r.relative(e,t))}function C(e,t,n,r=[]){let i=t.flatMap(t=>_e(e,t,n,r));return Array.from(new Set(i)).sort((e,t)=>e.localeCompare(t))}function _e(e,t,i,a){let o=r.resolve(e,t);if(t.includes(`*`))return ye(e,t).filter(e=>!a.some(t=>y(e,t))).filter(e=>r.basename(e)===i);if(!n.existsSync(o))return[];let s=n.statSync(o);return s.isFile()?r.basename(o)===i?[o]:[]:s.isDirectory()?ve(o,i,a):[]}function ve(e,t,i){let a=[],o=n.readdirSync(e,{withFileTypes:!0});for(let n of o){if(xe(n.name))continue;let o=r.join(e,n.name);if(!i.some(e=>y(o,e))){if(n.isDirectory()){a.push(...ve(o,t,i));continue}n.isFile()&&n.name===t&&a.push(o)}}return a}function ye(e,t){let i=r.resolve(e,t).split(r.sep);if(i.filter(e=>e.includes(`*`)).length!==1)throw Error(`Archicat supports exactly one wildcard segment per include pattern: ${t}`);let a=i.findIndex(e=>e.includes(`*`)),o=i.slice(0,a).join(r.sep)||r.sep,s=i[a]??`*`,c=i.slice(a+1),l=be(s);return n.existsSync(o)?n.readdirSync(o,{withFileTypes:!0}).filter(e=>e.isDirectory()).filter(e=>l.test(e.name)).map(e=>r.join(o,e.name,...c)).filter(e=>n.existsSync(e)&&n.statSync(e).isFile()):[]}function be(e){let t=e.replace(/[.+?^${}()|[\]\\]/gu,`\\$&`).replace(/\*/gu,`.*`);return RegExp(`^${t}$`,`u`)}function xe(e){return o.generated.ignoredDirectoryNames.includes(e)}async function w(e,t){switch(t){case`module`:return Se(e);case`library`:return Ce(e);case`app`:return we(e)}}async function Se(e){let t=await T(e,r.dirname(e));return E(t,e,`module`),{kind:`module`,contractFilePath:e,definitionDir:r.dirname(e),contract:t}}async function Ce(e){let t=await T(e,r.dirname(e));return E(t,e,`library`),{kind:`library`,contractFilePath:e,definitionDir:r.dirname(e),contract:t}}async function we(e){let t=await T(e,r.dirname(e));return E(t,e,`app`),{kind:`app`,contractFilePath:e,definitionDir:r.dirname(e),contract:t}}async function T(e,t){return await a(t,{interopDefault:!0,extensions:[`.js`,`.cjs`,`.mjs`,`.ts`,`.cts`,`.mts`,`.json`]}).import(e,{default:!0})}function E(e,t,n){if(typeof e!=`object`||!e)throw Error(`Invalid Archicat ${n} definition: ${t}`);let r=e;if(r.kind!==n)throw Error(`Archicat ${n} file must export a ${n} definition: ${t}`);if(typeof r.name!=`string`||r.name.trim()===``)throw Error(`Archicat ${n} must define a non-empty name: ${t}`);if(n===`app`){k(r.root,`root`,t),O(r.dependencies,t,n);return}let i=r;D(i.api,`api`,t,n),D(i.impl,`impl`,t,n)}function D(e,t,n,r){if(typeof e!=`object`||!e)throw Error(`Archicat ${r}.${t} must be a surface object: ${n}`);k(e.root,`${t}.root`,n),O(e.dependencies,n,`${r}.${t}`)}function O(e,t,n){if(!Array.isArray(e)||e.some(e=>typeof e!=`string`||e.trim()===``))throw Error(`Archicat ${n} dependencies must be an array of non-empty strings: ${t}`)}function k(e,t,n){if(e!==void 0&&(typeof e!=`string`||e.trim()===``))throw Error(`Archicat ${t} must be a non-empty string when defined: ${n}`)}function Te(e){let t=new Map;for(let n of e){let e=t.get(n.from)??[];e.push(n.to),t.set(n.from,e)}let n=new Set,r=new Set;for(let e of t.keys())A(e,t,n,r,[])}function A(e,t,n,r,i){if(!r.has(e)){if(n.has(e)){let t=i.indexOf(e),n=[...i.slice(t<0?0:t),e];throw Error(`Cyclic Archicat dependency detected: ${n.join(` -> `)}`)}n.add(e);for(let a of t.get(e)??[])A(a,t,n,r,[...i,e]);n.delete(e),r.add(e)}}function Ee(e){let t=/^(module|library)\.([a-z][a-z0-9-]*)\.(api|impl)$/u.exec(e);if(t)return{kind:t[1],name:t[2],surface:t[3]}}function j(e,t,n){let r=Ee(t);if(!r)throw Error(`${M(e)} declares invalid dependency target "${t}".`);if(!n.has(t))throw Error(`${M(e)} declares unknown dependency "${t}".`);if(e.target===t)throw Error(`${M(e)} cannot depend on itself: ${t}`);if(e.surface===`api`&&r.surface===`impl`)throw Error(`${M(e)} cannot depend on implementation target "${t}" from an API surface.`)}function M(e){return e.kind===`app`?`App "${e.name}"`:`${De(e.kind)} "${e.name}" ${e.surface}`}function De(e){return`${e.charAt(0).toUpperCase()}${e.slice(1)}`}function Oe(e,t){let n=t.filter(e=>e.kind===`module`).map(t=>ke(e,t)),r=t.filter(e=>e.kind===`library`).map(t=>Ae(e,t)),i=t.filter(e=>e.kind===`app`).map(e=>je(e)),a=[...n,...r];Ne([...a,...i]);let o=Me(a,i);return Pe(a,i,o.targets),Te(o.dependencies),{rootDir:e.rootDir,outDir:e.outDir,reportsDir:e.reportsDir,...e.tsconfigPath?{tsconfigPath:e.tsconfigPath}:{},configFilePath:e.configFilePath,config:e.resolvedConfig,modules:n,libraries:r,apps:i,definitions:a,graph:o}}function ke(e,t){let{contract:n,contractFilePath:i,definitionDir:a}=t;F(n.name,i,`module`);let s=n.api.root?P(a,n.api.root,`api`,n.name):void 0,c=n.impl.root?P(a,n.impl.root,`impl`,n.name):void 0,l=`${e.resolvedConfig.modules.alias}/${n.name}`,u=`${l}/api`,d=`${l}/impl`;return{kind:`module`,name:n.name,apiTarget:`module.${n.name}.api`,implTarget:`module.${n.name}.impl`,alias:u,aliasGlob:`${u}/*`,implAlias:d,implAliasGlob:`${d}/*`,contractFilePath:i,definitionDir:a,api:N(s,n.api.dependencies,r.join(e.outDir,o.generated.modulesDirName,n.name,`api`)),impl:N(c,n.impl.dependencies,r.join(e.outDir,o.generated.modulesDirName,n.name,`impl`))}}function Ae(e,t){let{contract:n,contractFilePath:i,definitionDir:a}=t;F(n.name,i,`library`);let s=n.api.root?P(a,n.api.root,`api`,n.name):void 0,c=n.impl.root?P(a,n.impl.root,`impl`,n.name):void 0,l=`${e.resolvedConfig.libraries.alias}/${n.name}`,u=`${l}/api`,d=`${l}/impl`;return{kind:`library`,name:n.name,apiTarget:`library.${n.name}.api`,implTarget:`library.${n.name}.impl`,alias:u,aliasGlob:`${u}/*`,implAlias:d,implAliasGlob:`${d}/*`,contractFilePath:i,definitionDir:a,api:N(s,n.api.dependencies,r.join(e.outDir,o.generated.librariesDirName,n.name,`api`)),impl:N(c,n.impl.dependencies,r.join(e.outDir,o.generated.librariesDirName,n.name,`impl`))}}function je(e){let{contract:t,contractFilePath:n,definitionDir:r}=e;return F(t.name,n,`app`),{kind:`app`,name:t.name,target:`app.${t.name}`,contractFilePath:n,rootPath:t.root?P(r,t.root,`app`,t.name):r,dependencies:[...t.dependencies]}}function N(e,t,n){return{...e?{rootPath:e}:{},mirrorRootPath:n,dependencies:[...t]}}function P(e,t,i,a){let o=r.resolve(e,t);if(!n.existsSync(o))throw Error(`Definition "${a}" declares ${i} root that does not exist: ${o}`);if(!n.statSync(o).isDirectory())throw Error(`Definition "${a}" declares ${i} root that is not a directory: ${o}`);return o}function Me(e,t){let n=e.flatMap(e=>[{key:e.apiTarget,surface:`api`},{key:e.implTarget,surface:`impl`}]),r=[...e.flatMap(e=>[...e.api.dependencies.map(t=>({from:e.apiTarget,to:t,origin:`declared`})),...e.impl.dependencies.map(t=>({from:e.implTarget,to:t,origin:`declared`}))]),...t.flatMap(e=>e.dependencies.map(t=>({from:e.target,to:t,origin:`declared`})))];return{targets:n,dependencies:[...e.map(e=>({from:e.implTarget,to:e.apiTarget,origin:`derived`})),...r]}}function F(e,t,n){if(!/^[a-z][a-z0-9-]*$/u.test(e))throw Error(`Invalid Archicat ${n} name "${e}" in ${t}. Use ^[a-z][a-z0-9-]*$`)}function Ne(e){let t=new Map;for(let n of e){let e=`${n.kind}.${n.name}`,r=t.get(e);if(r)throw Error(`Duplicate Archicat ${n.kind} name "${n.name}" in ${r} and ${n.contractFilePath}`);t.set(e,n.contractFilePath)}}function Pe(e,t,n){let r=new Set(n.map(e=>e.key));for(let t of e){for(let e of t.api.dependencies)j(Fe(t,`api`),e,r);for(let e of t.impl.dependencies)j(Fe(t,`impl`),e,r)}for(let e of t)for(let t of e.dependencies)j({kind:`app`,name:e.name,surface:`app`,target:e.target},t,r)}function Fe(e,t){return{kind:e.kind,name:e.name,surface:t,target:t===`api`?e.apiTarget:e.implTarget}}async function Ie(e,t){let n=await ne(e,t),r=C(n.rootDir,n.resolvedConfig.modules.include,o.definitions.moduleFileName,[n.outDir]),i=C(n.rootDir,n.resolvedConfig.libraries.include,o.definitions.libraryFileName,[n.outDir]),a=C(n.rootDir,n.resolvedConfig.apps.include,o.definitions.appFileName,[n.outDir]);if(r.length===0&&i.length===0&&a.length===0)throw Error(`No Archicat definition files matched configured include roots.`);return Oe(n,[...await Promise.all(r.map(e=>w(e,`module`))),...await Promise.all(i.map(e=>w(e,`library`))),...await Promise.all(a.map(e=>w(e,`app`)))])}function Le(e){let t=[];Re(t,`Modules`,e.modules.length,e.modules,e.graph.dependencies),Re(t,`Libraries`,e.libraries.length,e.libraries,e.graph.dependencies),t.push(`Apps: ${e.apps.length}`),e.apps.length>0&&t.push(``);for(let n of e.apps)t.push(n.name),t.push(` app: ${n.target}`),I(t,e.graph.dependencies.filter(e=>e.from===n.target),` dependsOn`),t.push(``);return ze(t)}function Re(e,t,n,r,i){e.push(`${t}: ${n}`),r.length>0&&e.push(``);for(let t of r)e.push(t.name),e.push(` api: ${t.apiTarget}`),I(e,i.filter(e=>e.from===t.apiTarget),` api dependsOn`),e.push(` impl: ${t.implTarget}`),I(e,i.filter(e=>e.from===t.implTarget),` impl dependsOn`),e.push(``)}function I(e,t,n){if(t.length===0){e.push(`${n}: none`);return}e.push(`${n}:`);for(let n of t){let t=n.origin===`derived`?` (derived)`:``;e.push(` ${n.to}${t}`)}}function ze(e){for(;e.at(-1)===``;)e.pop();return e}async function Be(e,t){return{exitCode:0,lines:Le(await Ie(t,e.config)).map(e=>({kind:`info`,message:e}))}}function Ve(e){return[...He(e),...Xe(e)]}function He(e){let t=Ze(e);if(!n.existsSync(t))return[`Consumer tsconfig was not found: ${t}`];let r=[],i=Ue(t,r);if(!i)return r;let a=We(i,t,r);return a?(Ge(r,e,i),Ke(r,a),qe(r,a),Je(r,a),Ye(r,i),r):r}function Ue(e,t){try{return s(e)}catch(e){t.push(`Failed to parse consumer tsconfig: ${tt(e)}`);return}}function We(e,t,n){try{return c(e,t)}catch(e){n.push(tt(e));return}}function Ge(e,t,n){let r=$e(t);n.extends!==r&&e.push(`Consumer tsconfig should extend ${r} for generated Archicat aliases to work.`)}function Ke(e,t){let n=t.rootDir;(n===`src`||n===`./src`)&&e.push(`compilerOptions.rootDir is set to src. Generated Archicat files live outside src and may break tsc.`)}function qe(e,t){Object.hasOwn(t,`paths`)&&e.push(`compilerOptions.paths should move to archicat.config.ts alias. Consumer tsconfig paths can override generated Archicat aliases.`)}function Je(e,t){Object.hasOwn(t,`baseUrl`)&&e.push(`compilerOptions.baseUrl is not supported. Move aliases into archicat.config.ts alias.`)}function Ye(e,t){(t.include!==void 0||t.exclude!==void 0||t.files!==void 0)&&e.push(`Consumer tsconfig include/exclude/files should move to archicat.config.ts typescript.tsConfig so generated Archicat types stay included.`)}function Xe(e){let t=[];for(let i of e.definitions){let e=r.join(i.definitionDir,`api`),a=r.join(i.definitionDir,`impl`);!i.api.rootPath&&n.existsSync(e)&&t.push(`${L(i.kind)} "${i.name}" has a physical api directory but its contract omits api.root. Archicat treats the public API as empty.`),!i.impl.rootPath&&n.existsSync(a)&&t.push(`${L(i.kind)} "${i.name}" has a physical impl directory but its contract omits impl.root. Archicat treats the implementation as no-op.`)}return t}function Ze(e){return r.join(e.rootDir,o.typescript.consumerTsconfigFileName)}function Qe(e){return r.join(e.outDir,o.generated.tsconfigFileName)}function $e(e){let t=v(r.relative(e.rootDir,Qe(e)));return et(t)||(t=`./${t}`),t}function et(e){return e.startsWith(`./`)||e.startsWith(`../`)}function tt(e){return e instanceof Error?e.message:String(e)}function L(e){return`${e.charAt(0).toUpperCase()}${e.slice(1)}`}function nt(e,t){ge(e,t),rt(e,t),n.rmSync(t,{recursive:!0,force:!0}),n.mkdirSync(t,{recursive:!0})}function R(e,t){n.mkdirSync(r.dirname(e),{recursive:!0}),n.writeFileSync(e,t,`utf8`)}function z(e,t){R(e,`${JSON.stringify(t,null,2)}\n`)}function rt(e,t){if(!(!n.existsSync(t)||n.readdirSync(t).length===0)&&!it(e,t))throw Error(`Archicat refuses to replace a non-owned outDir: ${t}`)}function it(e,t){let i=r.join(t,o.generated.reportsDirName,o.generated.buildReportFileName);if(!n.existsSync(i))return!1;try{let a=JSON.parse(n.readFileSync(i,`utf8`));return a.generatedBy===`archicat`&&a.schemaVersion===2&&typeof a.outputs?.outDir==`string`&&r.resolve(e,a.outputs.outDir)===r.resolve(t)}catch{return!1}}function at(e){let t=e.graph.targets.map(e=>e.key),n=e.graph.targets.filter(e=>e.surface===`api`).map(e=>e.key),i=o.packageName,a=`import '${i}';
|
|
3
|
+
|
|
4
|
+
declare module '${i}' {
|
|
5
|
+
${B(`ArchicatModuleApiDependencies`,n)}
|
|
6
|
+
|
|
7
|
+
${B(`ArchicatModuleImplDependencies`,t)}
|
|
8
|
+
|
|
9
|
+
${B(`ArchicatLibraryApiDependencies`,n)}
|
|
10
|
+
|
|
11
|
+
${B(`ArchicatLibraryImplDependencies`,t)}
|
|
12
|
+
|
|
13
|
+
${B(`ArchicatAppDependencies`,t)}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export {};
|
|
17
|
+
`;R(r.join(e.outDir,o.generated.typesDirName,`graph.d.ts`),a)}function B(e,t){return` interface ${e} {\n${Array.from(new Set(t)).sort((e,t)=>e.localeCompare(t)).map(e=>` '${e}': true;`).join(`
|
|
18
|
+
`)}\n }`}function V(e){return n.existsSync(e)?n.statSync(e).isFile()?H(e)?[e]:[]:U(e).filter(H).sort((e,t)=>e.localeCompare(t)):[]}function H(e){return/\.(?:ts|mts|cts|tsx)$/u.test(e)&&!/\.d\.(?:ts|mts|cts)$/u.test(e)}function U(e){let t=[],i=n.readdirSync(e,{withFileTypes:!0});for(let n of i){let i=r.join(e,n.name);if(n.isDirectory()){t.push(...U(i));continue}n.isFile()&&t.push(i)}return t}function ot(e){let t=i.createSourceFile(e,n.readFileSync(e,`utf8`),i.ScriptTarget.Latest,!0),r=[],a=e=>{if(i.isImportDeclaration(e)&&i.isStringLiteral(e.moduleSpecifier)&&r.push({moduleSpecifier:e.moduleSpecifier.text,kind:`import`}),i.isExportDeclaration(e)&&e.moduleSpecifier&&i.isStringLiteral(e.moduleSpecifier)&&r.push({moduleSpecifier:e.moduleSpecifier.text,kind:`export`}),i.isCallExpression(e)&&e.expression.kind===i.SyntaxKind.ImportKeyword){let[t]=e.arguments;t&&i.isStringLiteral(t)&&r.push({moduleSpecifier:t.text,kind:`dynamic-import`})}i.forEachChild(e,a)};return a(t),r}function st(e){let t=i.createSourceFile(e,n.readFileSync(e,`utf8`),i.ScriptTarget.Latest,!0),r=!1,a=e=>{if(!r){if(i.isExportAssignment(e)&&!e.isExportEquals){r=!0;return}if(ct(e)){r=!0;return}if(i.isExportDeclaration(e)&&e.exportClause&&i.isNamedExports(e.exportClause))for(let t of e.exportClause.elements){let e=t.name.text,n=t.propertyName?.text;if(e==="default"||n==="default"){r=!0;return}}i.forEachChild(e,a)}};return a(t),r}function ct(e){let t=i.canHaveModifiers(e)?i.getModifiers(e):void 0;if(!t)return!1;let n=t.some(e=>e.kind===i.SyntaxKind.ExportKeyword),r=t.some(e=>e.kind===i.SyntaxKind.DefaultKeyword);return n&&r}function lt(e){for(let t of e)ut(t.api),dt(t)}function ut(e){if(!e.rootPath){W(r.join(e.mirrorRootPath,`index.ts`));return}let t=new Set;for(let n of V(e.rootPath)){let i=v(r.relative(e.rootPath,n));t.add(i),G(r.join(e.mirrorRootPath,i),n)}t.has(`index.ts`)||W(r.join(e.mirrorRootPath,`index.ts`))}function dt(e){let t=e.impl.rootPath?ft(e.impl.rootPath):void 0;if(t){G(r.join(e.impl.mirrorRootPath,`index.ts`),t);return}let n=e.kind===`module`?`ArchicatModuleImplementation`:`ArchicatLibraryImplementation`,i=`${K()}
|
|
19
|
+
export const ${n} = {
|
|
20
|
+
name: '${e.name}',
|
|
21
|
+
} as const;
|
|
22
|
+
|
|
23
|
+
export default ${n};
|
|
24
|
+
`;R(r.join(e.impl.mirrorRootPath,`index.ts`),i)}function W(e){R(e,`${K()}export {};\n`)}function G(e,t){let n=he(e,t),r=st(t)?`export { default } from '${n}';\n`:``;R(e,`${K()}
|
|
25
|
+
export * from '${n}';
|
|
26
|
+
${r}`)}function ft(e){for(let t of[`index.ts`,`index.mts`,`index.cts`,`index.tsx`]){let i=r.join(e,t);if(n.existsSync(i)&&n.statSync(i).isFile())return i}}function K(){return`// Mirrored by Archicat.
|
|
27
|
+
`}function pt(e){z(r.join(e.reportsDir,o.generated.buildReportFileName),mt(e)),z(r.join(e.reportsDir,o.generated.graphReportFileName),ht(e))}function mt(e){return{generatedBy:`archicat`,schemaVersion:2,aliases:{module:e.config.modules.alias,library:e.config.libraries.alias},outputs:{outDir:S(e.rootDir,e.outDir),reportsDir:S(e.rootDir,e.reportsDir)},targets:e.graph.targets.map(e=>e.key),definitions:[...e.definitions.map(t=>gt(e,t)),...e.apps.map(t=>({kind:t.kind,name:t.name,targets:{app:t.target},aliases:{},dependencies:t.dependencies,contractFilePath:S(e.rootDir,t.contractFilePath),source:{root:S(e.rootDir,t.rootPath)},mirror:{}}))],dependencies:e.graph.dependencies}}function ht(e){return{generatedBy:`archicat`,schemaVersion:1,targets:e.graph.targets.map(e=>e.key),dependencies:e.graph.dependencies}}function gt(e,t){return{kind:t.kind,name:t.name,targets:{api:t.apiTarget,impl:t.implTarget},aliases:{api:t.alias,impl:t.implAlias},dependencies:{api:t.api.dependencies,impl:t.impl.dependencies},contractFilePath:S(e.rootDir,t.contractFilePath),source:{root:S(e.rootDir,t.definitionDir),api:t.api.rootPath?S(e.rootDir,t.api.rootPath):void 0,impl:t.impl.rootPath?S(e.rootDir,t.impl.rootPath):void 0},mirror:{api:S(e.rootDir,t.api.mirrorRootPath),impl:S(e.rootDir,t.impl.mirrorRootPath)}}}function _t(e){Dt(e.tsconfigPath),z(r.join(e.outDir,o.generated.tsconfigFileName),vt(e))}function vt(e){let t={compilerOptions:yt(e),include:xt(e)},n=bt(e),r=St(e),i=Ct(e);return n&&(t.extends=n),r.length>0&&(t.exclude=r),i.length>0&&(t.files=i),t}function yt(e){let t=Tt(e),n=wt(e);return jt(e,t,n),{paths:{...t,...n}}}function bt(e){return e.tsconfigPath?J(e.outDir,e.tsconfigPath):void 0}function xt(e){return Et([...q(e,e.config.typescript.tsConfig.include),o.generated.typesInclude])}function St(e){return q(e,e.config.typescript.tsConfig.exclude)}function Ct(e){return q(e,e.config.typescript.tsConfig.files)}function wt(e){let t={};for(let n of e.definitions)n.api.rootPath&&(t[n.aliasGlob]=[J(e.outDir,r.join(n.api.rootPath,`*`))]),n.impl.rootPath&&(t[n.implAliasGlob]=[J(e.outDir,r.join(n.impl.rootPath,`*`))]);return t}function Tt(e){let t={};for(let[n,i]of Object.entries(e.config.alias)){let a=r.isAbsolute(i)?i:r.resolve(e.rootDir,i);t[n]=[J(e.outDir,a)]}return t}function q(e,t){return t.map(t=>{let n=r.isAbsolute(t)?t:r.resolve(e.rootDir,t);return J(e.outDir,n)})}function J(e,t){let n=v(r.relative(e,t));return n.startsWith(`.`)||(n=`./${n}`),n}function Et(e){return[...new Set(e)]}function Dt(e){e&&Ot(e,new Set,new Set)}function Ot(e,t,n){let i=r.resolve(e);if(n.has(i))return;if(t.has(i))throw Error(`Circular tsconfig extends chain detected: ${i}`);t.add(i);let a=s(i),o=c(a,i);kt(o,i),At(o,i);for(let e of l(a,i))Ot(e,t,n);t.delete(i),n.add(i)}function kt(e,t){if(Object.hasOwn(e,`paths`))throw Error(`Base tsconfig compilerOptions.paths is not supported by Archicat. Move aliases into archicat.config.ts alias: ${t}`)}function At(e,t){if(Object.hasOwn(e,`baseUrl`))throw Error(`Base tsconfig compilerOptions.baseUrl is not supported by Archicat. Move aliases into archicat.config.ts alias: ${t}`)}function jt(e,t,n){let r=Object.keys(t),i=Object.keys(n),a=[e.config.modules.alias,e.config.libraries.alias];for(let e of r){if(i.includes(e))throw Error(`Alias conflict: archicat.config.ts alias already defines "${e}", but Archicat needs it.`);for(let t of a)if(e===t||e===`${t}/*`||e.startsWith(`${t}/`))throw Error(`Alias conflict: archicat.config.ts alias "${e}" is inside Archicat reserved alias "${t}". Remove the alias or configure another module/library alias.`)}}function Mt(e){nt(e.rootDir,e.outDir),n.mkdirSync(r.join(e.outDir,o.generated.modulesDirName),{recursive:!0}),n.mkdirSync(r.join(e.outDir,o.generated.librariesDirName),{recursive:!0}),n.mkdirSync(r.join(e.outDir,o.generated.typesDirName),{recursive:!0}),n.mkdirSync(e.reportsDir,{recursive:!0}),lt(e.definitions),at(e),_t(e),pt(e)}function Nt(e){let t=Bt(e.graph.dependencies),n=[],r=[...e.definitions.flatMap(e=>[...e.api.rootPath?V(e.api.rootPath):[],...e.impl.rootPath?V(e.impl.rootPath):[]]),...e.apps.flatMap(e=>V(e.rootPath))];for(let i of r){let r=It(e,i);if(r)for(let a of ot(i)){let o=Ft(e,t,r,i,a.moduleSpecifier);o&&n.push(o)}}return n}function Pt(e){return`${e.filePath}\n import: ${e.importPath}\n ${e.message}`}function Ft(e,t,n,r,i){let a=Rt(e,i);if(a)return Y(e,r,i,`Unsupported Archicat alias "${i}". Use an explicit file import under "${a.apiAlias}/*" or "${a.implAlias}/*".`);let o=Lt(e,i);if(o)return zt(n,o)||Vt(t,n.target,o.target)?void 0:Y(e,r,i,`${X(n)} imports "${o.target}" but does not declare a dependency that allows it.`);if(!i.startsWith(`.`)&&!i.startsWith(`/`))return;let s=It(e,Ht(r,i));if(!(!s||s.target===n.target))return Y(e,r,i,`${X(n)} imports ${X(s)} through a source path. Use an Archicat alias instead.`)}function It(e,t){let n=x(t);for(let t of e.definitions){if(t.api.rootPath&&y(n,x(t.api.rootPath)))return{kind:t.kind,name:t.name,surface:`api`,target:t.apiTarget};if(t.impl.rootPath&&y(n,x(t.impl.rootPath)))return{kind:t.kind,name:t.name,surface:`impl`,target:t.implTarget}}for(let t of e.apps)if(y(n,x(t.rootPath)))return{kind:`app`,name:t.name,surface:`app`,target:t.target}}function Lt(e,t){for(let n of e.definitions){if(t.startsWith(`${n.implAlias}/`))return{kind:n.kind,name:n.name,surface:`impl`,target:n.implTarget};if(t.startsWith(`${n.alias}/`))return{kind:n.kind,name:n.name,surface:`api`,target:n.apiTarget}}}function Rt(e,t){for(let n of e.definitions){let r=`${n.kind===`module`?e.config.modules.alias:e.config.libraries.alias}/${n.name}`,i=n.implAlias;if(t===r||t===n.alias||t===i||t.startsWith(`${r}/`)&&!t.startsWith(`${n.alias}/`)&&!t.startsWith(`${i}/`))return{apiAlias:n.alias,implAlias:i}}}function zt(e,t){return e.kind===t.kind&&e.name===t.name&&t.surface===`api`}function Bt(e){let t=new Map;for(let n of e){let e=t.get(n.from)??[];e.push(n.to),t.set(n.from,e)}return t}function Vt(e,t,n){let r=new Set,i=[t];for(;i.length>0;){let t=i.shift();if(t){if(t===n)return!0;r.has(t)||(r.add(t),i.push(...e.get(t)??[]))}}return!1}function Ht(e,t){return x(t.startsWith(`/`)?t:r.resolve(r.dirname(e),t))}function Y(e,t,n,r){return{filePath:S(e.rootDir,t),importPath:n,message:r}}function X(e){return e.kind===`app`?`App "${e.name}"`:`${Ut(e.kind)} "${e.name}" ${e.surface}`}function Ut(e){return`${e.charAt(0).toUpperCase()}${e.slice(1)}`}const Wt=new t;async function Z(e,t,n){let r=Date.now(),i=await Ie(n,t.config),a=Jt(e,n,i),o=[],s=[];if((e===`doctor`||e===`generate`)&&s.push(...Gt(i)),e===`validate`||e===`generate`){let e=Kt(i);if(s.push(...e.lines),e.exitCode!==0)return qt(a,o,s,e.exitCode,r)}return e===`generate`&&(Mt(i),o.push({kind:`panel`,title:`mirrored`,rows:[{label:`modules`,value:i.modules.length},{label:`libraries`,value:i.libraries.length},{label:`apps`,value:i.apps.length}]})),qt(a,o,s,0,r)}function Gt(e){let t=Ve(e);return t.length===0?[{kind:`success`,label:`doctor`,message:`Project diagnostics passed`}]:[{kind:`warning`,label:`doctor`,message:`Project diagnostics completed with warnings`},...t.map(e=>({kind:`warning`,message:` ${e}`}))]}function Kt(e){let t=Nt(e);return t.length===0?{exitCode:0,lines:[{kind:`success`,label:`validate`,message:`Architecture boundaries passed`}]}:{exitCode:1,lines:[{kind:`error`,label:`validate`,message:`Architecture validation failed`},...t.map(e=>({kind:`error`,message:Pt(e)}))]}}function qt(e,t,n,r,i){let a=r===0?{kind:`success`,label:`done`,message:`Completed in ${Wt.duration(Date.now()-i)}`}:{kind:`error`,label:`failed`,message:`Failed in ${Wt.duration(Date.now()-i)}`};return{exitCode:r,lines:[e,...t,...n,a,{kind:`info`,message:``}]}}function Jt(e,t,n){return{kind:`title`,product:`ArchiCat`,command:e,rows:[{label:`config`,value:Yt(t,n.configFilePath)},{label:`output`,value:Yt(t,n.outDir)}]}}function Yt(e,t){let n=r.relative(e,t);return!n||n.startsWith(`..`)?t:n}const Q=new t;async function Xt(e=process.argv.slice(2),t=process.cwd()){let[n,...r]=e;try{let e=await Zt(n,tn(r),t);if(!e)return;Qt(e),process.exitCode=e.exitCode}catch(e){Q.error(e instanceof Error?e.message:String(e)),process.exitCode=1}}async function Zt(e,t,n){switch(e){case`generate`:return await Z(`generate`,t,n);case`validate`:case`check`:return await Z(`validate`,t,n);case`graph`:return await Be(t,n);case`doctor`:return await Z(`doctor`,t,n);case`help`:case`--help`:case`-h`:case void 0:en();return;default:return Q.error(`Unknown command: ${e}`),en(),{exitCode:1,lines:[]}}}function Qt(e){for(let t of e.lines)$t(t)}function $t(e){switch(e.kind){case`title`:Q.title(e.product,e.command,e.rows??[]);return;case`panel`:Q.panel(e.title,e.rows,e.badge);return;case`success`:Q.success($(e));return;case`info`:if(e.message.length===0){Q.emptyLine();return}Q.info($(e));return;case`warning`:Q.warn($(e));return;case`error`:Q.error($(e));return}}function $(e){return e.label?Q.step(e.label,e.message):e.message}function en(){Q.log([`ArchiCat`,``,`Usage:`,` archicat generate [--config archicat.config.ts]`,` archicat validate [--config archicat.config.ts]`,` archicat graph [--config archicat.config.ts]`,` archicat doctor [--config archicat.config.ts]`,``,`Aliases:`,` archicat check -> archicat validate`,``].join(`
|
|
28
|
+
`))}function tn(e){let t={};for(let n=0;n<e.length;n+=1){let r=e[n];if(r===`--config`||r===`-c`){let i=e[n+1];if(!i)throw Error(`${r} requires a value.`);t.config=i,n+=1}}return t}export{Xt as runMain};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});function e(e){return Object.freeze({kind:`app`,name:e.name,...e.root===void 0?{}:{root:e.root},dependencies:Object.freeze([...e.dependencies??[]])})}function t(e){let t={...e};for(let e of Object.keys(t))t[e]===void 0&&delete t[e];return Object.freeze(t)}function n(e={}){return t({root:e.root,outDir:e.outDir,typescript:e.typescript?r(e.typescript):void 0,alias:e.alias?Object.freeze({...e.alias}):void 0,modules:e.modules?a(e.modules):void 0,libraries:e.libraries?o(e.libraries):void 0,apps:e.apps?s(e.apps):void 0})}function r(e){return t({tsConfig:e.tsConfig?i(e.tsConfig):void 0})}function i(e){return t({extends:e.extends,include:e.include?Object.freeze([...e.include]):void 0,exclude:e.exclude?Object.freeze([...e.exclude]):void 0,files:e.files?Object.freeze([...e.files]):void 0,compilerOptions:e.compilerOptions?Object.freeze({...e.compilerOptions}):void 0})}function a(e){return t({include:e.include?Object.freeze([...e.include]):void 0,alias:e.alias})}function o(e){return t({include:e.include?Object.freeze([...e.include]):void 0,alias:e.alias})}function s(e){return t({include:e.include?Object.freeze([...e.include]):void 0})}function c(e){return typeof e==`string`?Object.freeze({root:e,dependencies:Object.freeze([])}):t({root:e?.root,dependencies:Object.freeze([...e?.dependencies??[]])})}function l(e){return Object.freeze({kind:`library`,name:e.name,api:c(e.api),impl:c(e.impl)})}function u(e){return Object.freeze({kind:`module`,name:e.name,api:c(e.api),impl:c(e.impl)})}exports.defineApp=e,exports.defineArchicatConfig=n,exports.defineLibrary=l,exports.defineModule=u;
|
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
//#region src/configs/archicat-project-graph.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* @description Generated dependency targets allowed in module API surfaces.
|
|
4
|
+
*/
|
|
5
|
+
interface ArchicatModuleApiDependencies {}
|
|
6
|
+
/**
|
|
7
|
+
* @description Generated dependency targets allowed in module implementation surfaces.
|
|
8
|
+
*/
|
|
9
|
+
interface ArchicatModuleImplDependencies {}
|
|
10
|
+
/**
|
|
11
|
+
* @description Generated dependency targets allowed in library API surfaces.
|
|
12
|
+
*/
|
|
13
|
+
interface ArchicatLibraryApiDependencies {}
|
|
14
|
+
/**
|
|
15
|
+
* @description Generated dependency targets allowed in library implementation surfaces.
|
|
16
|
+
*/
|
|
17
|
+
interface ArchicatLibraryImplDependencies {}
|
|
18
|
+
/**
|
|
19
|
+
* @description Generated dependency targets allowed in app composition roots.
|
|
20
|
+
*/
|
|
21
|
+
interface ArchicatAppDependencies {}
|
|
22
|
+
/**
|
|
23
|
+
* @description Dependency key fallback used before `.archicat/types/graph.d.ts` exists.
|
|
24
|
+
*/
|
|
25
|
+
type ArchicatDependencyKey<Dependencies> = keyof Dependencies extends never ? string : Extract<keyof Dependencies, string>;
|
|
26
|
+
/**
|
|
27
|
+
* @description Dependency target allowed from a module API surface.
|
|
28
|
+
*/
|
|
29
|
+
type ArchicatModuleApiDependency = ArchicatDependencyKey<ArchicatModuleApiDependencies>;
|
|
30
|
+
/**
|
|
31
|
+
* @description Dependency target allowed from a module implementation surface.
|
|
32
|
+
*/
|
|
33
|
+
type ArchicatModuleImplDependency = ArchicatDependencyKey<ArchicatModuleImplDependencies>;
|
|
34
|
+
/**
|
|
35
|
+
* @description Dependency target allowed from a library API surface.
|
|
36
|
+
*/
|
|
37
|
+
type ArchicatLibraryApiDependency = ArchicatDependencyKey<ArchicatLibraryApiDependencies>;
|
|
38
|
+
/**
|
|
39
|
+
* @description Dependency target allowed from a library implementation surface.
|
|
40
|
+
*/
|
|
41
|
+
type ArchicatLibraryImplDependency = ArchicatDependencyKey<ArchicatLibraryImplDependencies>;
|
|
42
|
+
/**
|
|
43
|
+
* @description Dependency target allowed from an app composition root.
|
|
44
|
+
*/
|
|
45
|
+
type ArchicatAppDependency = ArchicatDependencyKey<ArchicatAppDependencies>;
|
|
46
|
+
//#endregion
|
|
47
|
+
//#region src/configs/app-config.d.ts
|
|
48
|
+
/**
|
|
49
|
+
* @description User-facing app composition definition input.
|
|
50
|
+
*/
|
|
51
|
+
interface ArchicatAppInput {
|
|
52
|
+
/**
|
|
53
|
+
* @description Stable app name used in the Archicat project graph.
|
|
54
|
+
*/
|
|
55
|
+
readonly name: string;
|
|
56
|
+
/**
|
|
57
|
+
* @description App source root, relative to the app definition file.
|
|
58
|
+
* @default The directory containing `archicat.app.ts`.
|
|
59
|
+
*/
|
|
60
|
+
readonly root?: string;
|
|
61
|
+
/**
|
|
62
|
+
* @description Dependency targets visible from this app composition root.
|
|
63
|
+
* @default []
|
|
64
|
+
*/
|
|
65
|
+
readonly dependencies?: readonly ArchicatAppDependency[];
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* @description Immutable app composition contract loaded by Archicat.
|
|
69
|
+
*/
|
|
70
|
+
type ArchicatAppContract = Readonly<{
|
|
71
|
+
readonly kind: 'app';
|
|
72
|
+
readonly name: string;
|
|
73
|
+
readonly root?: string;
|
|
74
|
+
readonly dependencies: readonly ArchicatAppDependency[];
|
|
75
|
+
}>;
|
|
76
|
+
//#endregion
|
|
77
|
+
//#region src/configs/archicat-config.d.ts
|
|
78
|
+
/**
|
|
79
|
+
* @description TypeScript config fragment merged into generated `.archicat/tsconfig.json`.
|
|
80
|
+
*/
|
|
81
|
+
interface TsConfigInput {
|
|
82
|
+
/**
|
|
83
|
+
* @description Base TypeScript config extended by generated `.archicat/tsconfig.json`.
|
|
84
|
+
*/
|
|
85
|
+
readonly extends?: string;
|
|
86
|
+
/**
|
|
87
|
+
* @description Project source files merged into generated `.archicat/tsconfig.json` include.
|
|
88
|
+
* Archicat rewrites relative paths to the generated config directory and appends generated type declarations automatically.
|
|
89
|
+
*/
|
|
90
|
+
readonly include?: readonly string[];
|
|
91
|
+
/**
|
|
92
|
+
* @description Project paths merged into generated `.archicat/tsconfig.json` exclude.
|
|
93
|
+
* Archicat rewrites relative paths to the generated config directory.
|
|
94
|
+
*/
|
|
95
|
+
readonly exclude?: readonly string[];
|
|
96
|
+
/**
|
|
97
|
+
* @description Project files merged into generated `.archicat/tsconfig.json` files.
|
|
98
|
+
* Archicat rewrites relative paths to the generated config directory.
|
|
99
|
+
*/
|
|
100
|
+
readonly files?: readonly string[];
|
|
101
|
+
/**
|
|
102
|
+
* @description Unsupported. Put compiler options in the base or app tsconfig. Archicat owns generated `compilerOptions.paths` and does not support `baseUrl`.
|
|
103
|
+
*/
|
|
104
|
+
readonly compilerOptions?: {
|
|
105
|
+
readonly paths?: never;
|
|
106
|
+
readonly baseUrl?: never;
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* @description TypeScript integration options used to generate `.archicat/tsconfig.json`.
|
|
111
|
+
*/
|
|
112
|
+
interface TypeScriptConfigInput {
|
|
113
|
+
/**
|
|
114
|
+
* @description Partial TypeScript config merged into generated `.archicat/tsconfig.json`.
|
|
115
|
+
* Archicat rewrites relative paths from the project root to `.archicat`, appends generated type declarations to `include`, and injects generated aliases into `compilerOptions.paths`.
|
|
116
|
+
* `compilerOptions` is not supported here. Put compiler policy in the base tsconfig and app build overrides in the app tsconfig.
|
|
117
|
+
* @default {}
|
|
118
|
+
*/
|
|
119
|
+
readonly tsConfig?: TsConfigInput;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* @description User import aliases generated into `.archicat/tsconfig.json` together with Archicat module and library aliases.
|
|
123
|
+
* Aliases are resolved relative to the Archicat project root. Do not define TypeScript `compilerOptions.paths` manually for an Archicat project.
|
|
124
|
+
* @default {}
|
|
125
|
+
*/
|
|
126
|
+
type AliasConfig = Readonly<Record<string, string>>;
|
|
127
|
+
/**
|
|
128
|
+
* @description Module discovery and generated import alias configuration.
|
|
129
|
+
*/
|
|
130
|
+
interface ModulesConfigInput {
|
|
131
|
+
/**
|
|
132
|
+
* @description Directories or files where Archicat searches for module definition markers.
|
|
133
|
+
*/
|
|
134
|
+
readonly include?: readonly string[];
|
|
135
|
+
/**
|
|
136
|
+
* @description Base alias used for generated module imports.
|
|
137
|
+
*/
|
|
138
|
+
readonly alias?: string;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* @description Library discovery and generated import alias configuration.
|
|
142
|
+
*/
|
|
143
|
+
interface LibrariesConfigInput {
|
|
144
|
+
/**
|
|
145
|
+
* @description Directories or files where Archicat searches for library definition markers.
|
|
146
|
+
*/
|
|
147
|
+
readonly include?: readonly string[];
|
|
148
|
+
/**
|
|
149
|
+
* @description Base alias used for generated library imports.
|
|
150
|
+
*/
|
|
151
|
+
readonly alias?: string;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* @description App discovery configuration.
|
|
155
|
+
*/
|
|
156
|
+
interface AppsConfigInput {
|
|
157
|
+
/**
|
|
158
|
+
* @description Directories or files where Archicat searches for app definition markers.
|
|
159
|
+
*/
|
|
160
|
+
readonly include?: readonly string[];
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* @description User-facing Archicat root config input.
|
|
164
|
+
*/
|
|
165
|
+
interface ArchicatConfigInput {
|
|
166
|
+
/**
|
|
167
|
+
* @description Project root directory.
|
|
168
|
+
* @default '.'
|
|
169
|
+
*/
|
|
170
|
+
readonly root?: string;
|
|
171
|
+
/**
|
|
172
|
+
* @description Directory for generated mirror files, generated types, and reports.
|
|
173
|
+
* @default '.archicat'
|
|
174
|
+
*/
|
|
175
|
+
readonly outDir?: string;
|
|
176
|
+
/**
|
|
177
|
+
* @description TypeScript integration options used to generate `.archicat/tsconfig.json`.
|
|
178
|
+
* @default {}
|
|
179
|
+
*/
|
|
180
|
+
readonly typescript?: TypeScriptConfigInput;
|
|
181
|
+
/**
|
|
182
|
+
* @description User TypeScript aliases generated by Archicat.
|
|
183
|
+
* @default {}
|
|
184
|
+
*/
|
|
185
|
+
readonly alias?: AliasConfig;
|
|
186
|
+
/**
|
|
187
|
+
* @description Module discovery and alias config.
|
|
188
|
+
* @default { include: ['./src/modules'], alias: '#modules' }
|
|
189
|
+
*/
|
|
190
|
+
readonly modules?: ModulesConfigInput;
|
|
191
|
+
/**
|
|
192
|
+
* @description Library discovery and alias config.
|
|
193
|
+
* @default { include: [], alias: '#library' }
|
|
194
|
+
*/
|
|
195
|
+
readonly libraries?: LibrariesConfigInput;
|
|
196
|
+
/**
|
|
197
|
+
* @description App discovery config.
|
|
198
|
+
* @default { include: [] }
|
|
199
|
+
*/
|
|
200
|
+
readonly apps?: AppsConfigInput;
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* @description Immutable Archicat config returned by `defineArchicatConfig`.
|
|
204
|
+
*/
|
|
205
|
+
type ArchicatConfig = Readonly<ArchicatConfigInput>;
|
|
206
|
+
//#endregion
|
|
207
|
+
//#region src/configs/surface-config.d.ts
|
|
208
|
+
/**
|
|
209
|
+
* @description Surface root shorthand or full surface config.
|
|
210
|
+
*/
|
|
211
|
+
type ArchicatSurfaceInput<Dependency extends string> = string | ArchicatSurfaceConfig<Dependency>;
|
|
212
|
+
/**
|
|
213
|
+
* @description Source surface config.
|
|
214
|
+
*/
|
|
215
|
+
interface ArchicatSurfaceConfig<Dependency extends string> {
|
|
216
|
+
/**
|
|
217
|
+
* @description Surface source root, relative to the definition file.
|
|
218
|
+
* @default Generates an empty mirror for the surface.
|
|
219
|
+
*/
|
|
220
|
+
readonly root?: string;
|
|
221
|
+
/**
|
|
222
|
+
* @description Dependency targets visible from this surface.
|
|
223
|
+
* @default []
|
|
224
|
+
*/
|
|
225
|
+
readonly dependencies?: readonly Dependency[];
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* @description Immutable normalized surface contract.
|
|
229
|
+
*/
|
|
230
|
+
type ArchicatSurfaceContract<Dependency extends string> = Readonly<{
|
|
231
|
+
readonly root?: string;
|
|
232
|
+
readonly dependencies: readonly Dependency[];
|
|
233
|
+
}>;
|
|
234
|
+
//#endregion
|
|
235
|
+
//#region src/configs/library-config.d.ts
|
|
236
|
+
/**
|
|
237
|
+
* @description User-facing library definition input.
|
|
238
|
+
*/
|
|
239
|
+
interface ArchicatLibraryInput {
|
|
240
|
+
/**
|
|
241
|
+
* @description Stable library name used in the Archicat project graph.
|
|
242
|
+
*/
|
|
243
|
+
readonly name: string;
|
|
244
|
+
/**
|
|
245
|
+
* @description Library public API surface.
|
|
246
|
+
* @default Generates an empty public API mirror.
|
|
247
|
+
*/
|
|
248
|
+
readonly api?: ArchicatSurfaceInput<ArchicatLibraryApiDependency>;
|
|
249
|
+
/**
|
|
250
|
+
* @description Library implementation surface.
|
|
251
|
+
* @default Generates a no-op implementation mirror.
|
|
252
|
+
*/
|
|
253
|
+
readonly impl?: ArchicatSurfaceInput<ArchicatLibraryImplDependency>;
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* @description Immutable library definition contract loaded by Archicat.
|
|
257
|
+
*/
|
|
258
|
+
type ArchicatLibraryContract = Readonly<{
|
|
259
|
+
readonly kind: 'library';
|
|
260
|
+
readonly name: string;
|
|
261
|
+
readonly api: ArchicatSurfaceContract<ArchicatLibraryApiDependency>;
|
|
262
|
+
readonly impl: ArchicatSurfaceContract<ArchicatLibraryImplDependency>;
|
|
263
|
+
}>;
|
|
264
|
+
//#endregion
|
|
265
|
+
//#region src/configs/module-config.d.ts
|
|
266
|
+
/**
|
|
267
|
+
* @description User-facing module definition input.
|
|
268
|
+
*/
|
|
269
|
+
interface ArchicatModuleInput {
|
|
270
|
+
/**
|
|
271
|
+
* @description Stable module name used in the Archicat project graph.
|
|
272
|
+
*/
|
|
273
|
+
readonly name: string;
|
|
274
|
+
/**
|
|
275
|
+
* @description Module public API surface.
|
|
276
|
+
* @default Generates an empty public API mirror.
|
|
277
|
+
*/
|
|
278
|
+
readonly api?: ArchicatSurfaceInput<ArchicatModuleApiDependency>;
|
|
279
|
+
/**
|
|
280
|
+
* @description Module implementation surface.
|
|
281
|
+
* @default Generates a no-op implementation mirror.
|
|
282
|
+
*/
|
|
283
|
+
readonly impl?: ArchicatSurfaceInput<ArchicatModuleImplDependency>;
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* @description Immutable module definition contract loaded by Archicat.
|
|
287
|
+
*/
|
|
288
|
+
type ArchicatModuleContract = Readonly<{
|
|
289
|
+
readonly kind: 'module';
|
|
290
|
+
readonly name: string;
|
|
291
|
+
readonly api: ArchicatSurfaceContract<ArchicatModuleApiDependency>;
|
|
292
|
+
readonly impl: ArchicatSurfaceContract<ArchicatModuleImplDependency>;
|
|
293
|
+
}>;
|
|
294
|
+
//#endregion
|
|
295
|
+
//#region src/configs/define-app-config.d.ts
|
|
296
|
+
/**
|
|
297
|
+
* @description Defines one Archicat app composition root.
|
|
298
|
+
*/
|
|
299
|
+
declare function defineApp(app: ArchicatAppInput): ArchicatAppContract;
|
|
300
|
+
//#endregion
|
|
301
|
+
//#region src/configs/define-archicat-config.d.ts
|
|
302
|
+
/**
|
|
303
|
+
* @description Defines the root Archicat config.
|
|
304
|
+
*/
|
|
305
|
+
declare function defineArchicatConfig(config?: ArchicatConfigInput): ArchicatConfig;
|
|
306
|
+
//#endregion
|
|
307
|
+
//#region src/configs/define-library-config.d.ts
|
|
308
|
+
/**
|
|
309
|
+
* @description Defines one Archicat library.
|
|
310
|
+
*/
|
|
311
|
+
declare function defineLibrary(library: ArchicatLibraryInput): ArchicatLibraryContract;
|
|
312
|
+
//#endregion
|
|
313
|
+
//#region src/configs/define-module-config.d.ts
|
|
314
|
+
/**
|
|
315
|
+
* @description Defines one Archicat module.
|
|
316
|
+
*/
|
|
317
|
+
declare function defineModule(module: ArchicatModuleInput): ArchicatModuleContract;
|
|
318
|
+
//#endregion
|
|
319
|
+
export { type AliasConfig, type AppsConfigInput, type ArchicatAppContract, type ArchicatAppDependencies, type ArchicatAppDependency, type ArchicatAppInput, type ArchicatConfig, type ArchicatConfigInput, type ArchicatLibraryApiDependencies, type ArchicatLibraryApiDependency, type ArchicatLibraryContract, type ArchicatLibraryImplDependencies, type ArchicatLibraryImplDependency, type ArchicatLibraryInput, type ArchicatModuleApiDependencies, type ArchicatModuleApiDependency, type ArchicatModuleContract, type ArchicatModuleImplDependencies, type ArchicatModuleImplDependency, type ArchicatModuleInput, type ArchicatSurfaceConfig, type ArchicatSurfaceContract, type ArchicatSurfaceInput, type LibrariesConfigInput, type ModulesConfigInput, type TsConfigInput, type TypeScriptConfigInput, defineApp, defineArchicatConfig, defineLibrary, defineModule };
|
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
//#region src/configs/archicat-project-graph.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* @description Generated dependency targets allowed in module API surfaces.
|
|
4
|
+
*/
|
|
5
|
+
interface ArchicatModuleApiDependencies {}
|
|
6
|
+
/**
|
|
7
|
+
* @description Generated dependency targets allowed in module implementation surfaces.
|
|
8
|
+
*/
|
|
9
|
+
interface ArchicatModuleImplDependencies {}
|
|
10
|
+
/**
|
|
11
|
+
* @description Generated dependency targets allowed in library API surfaces.
|
|
12
|
+
*/
|
|
13
|
+
interface ArchicatLibraryApiDependencies {}
|
|
14
|
+
/**
|
|
15
|
+
* @description Generated dependency targets allowed in library implementation surfaces.
|
|
16
|
+
*/
|
|
17
|
+
interface ArchicatLibraryImplDependencies {}
|
|
18
|
+
/**
|
|
19
|
+
* @description Generated dependency targets allowed in app composition roots.
|
|
20
|
+
*/
|
|
21
|
+
interface ArchicatAppDependencies {}
|
|
22
|
+
/**
|
|
23
|
+
* @description Dependency key fallback used before `.archicat/types/graph.d.ts` exists.
|
|
24
|
+
*/
|
|
25
|
+
type ArchicatDependencyKey<Dependencies> = keyof Dependencies extends never ? string : Extract<keyof Dependencies, string>;
|
|
26
|
+
/**
|
|
27
|
+
* @description Dependency target allowed from a module API surface.
|
|
28
|
+
*/
|
|
29
|
+
type ArchicatModuleApiDependency = ArchicatDependencyKey<ArchicatModuleApiDependencies>;
|
|
30
|
+
/**
|
|
31
|
+
* @description Dependency target allowed from a module implementation surface.
|
|
32
|
+
*/
|
|
33
|
+
type ArchicatModuleImplDependency = ArchicatDependencyKey<ArchicatModuleImplDependencies>;
|
|
34
|
+
/**
|
|
35
|
+
* @description Dependency target allowed from a library API surface.
|
|
36
|
+
*/
|
|
37
|
+
type ArchicatLibraryApiDependency = ArchicatDependencyKey<ArchicatLibraryApiDependencies>;
|
|
38
|
+
/**
|
|
39
|
+
* @description Dependency target allowed from a library implementation surface.
|
|
40
|
+
*/
|
|
41
|
+
type ArchicatLibraryImplDependency = ArchicatDependencyKey<ArchicatLibraryImplDependencies>;
|
|
42
|
+
/**
|
|
43
|
+
* @description Dependency target allowed from an app composition root.
|
|
44
|
+
*/
|
|
45
|
+
type ArchicatAppDependency = ArchicatDependencyKey<ArchicatAppDependencies>;
|
|
46
|
+
//#endregion
|
|
47
|
+
//#region src/configs/app-config.d.ts
|
|
48
|
+
/**
|
|
49
|
+
* @description User-facing app composition definition input.
|
|
50
|
+
*/
|
|
51
|
+
interface ArchicatAppInput {
|
|
52
|
+
/**
|
|
53
|
+
* @description Stable app name used in the Archicat project graph.
|
|
54
|
+
*/
|
|
55
|
+
readonly name: string;
|
|
56
|
+
/**
|
|
57
|
+
* @description App source root, relative to the app definition file.
|
|
58
|
+
* @default The directory containing `archicat.app.ts`.
|
|
59
|
+
*/
|
|
60
|
+
readonly root?: string;
|
|
61
|
+
/**
|
|
62
|
+
* @description Dependency targets visible from this app composition root.
|
|
63
|
+
* @default []
|
|
64
|
+
*/
|
|
65
|
+
readonly dependencies?: readonly ArchicatAppDependency[];
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* @description Immutable app composition contract loaded by Archicat.
|
|
69
|
+
*/
|
|
70
|
+
type ArchicatAppContract = Readonly<{
|
|
71
|
+
readonly kind: 'app';
|
|
72
|
+
readonly name: string;
|
|
73
|
+
readonly root?: string;
|
|
74
|
+
readonly dependencies: readonly ArchicatAppDependency[];
|
|
75
|
+
}>;
|
|
76
|
+
//#endregion
|
|
77
|
+
//#region src/configs/archicat-config.d.ts
|
|
78
|
+
/**
|
|
79
|
+
* @description TypeScript config fragment merged into generated `.archicat/tsconfig.json`.
|
|
80
|
+
*/
|
|
81
|
+
interface TsConfigInput {
|
|
82
|
+
/**
|
|
83
|
+
* @description Base TypeScript config extended by generated `.archicat/tsconfig.json`.
|
|
84
|
+
*/
|
|
85
|
+
readonly extends?: string;
|
|
86
|
+
/**
|
|
87
|
+
* @description Project source files merged into generated `.archicat/tsconfig.json` include.
|
|
88
|
+
* Archicat rewrites relative paths to the generated config directory and appends generated type declarations automatically.
|
|
89
|
+
*/
|
|
90
|
+
readonly include?: readonly string[];
|
|
91
|
+
/**
|
|
92
|
+
* @description Project paths merged into generated `.archicat/tsconfig.json` exclude.
|
|
93
|
+
* Archicat rewrites relative paths to the generated config directory.
|
|
94
|
+
*/
|
|
95
|
+
readonly exclude?: readonly string[];
|
|
96
|
+
/**
|
|
97
|
+
* @description Project files merged into generated `.archicat/tsconfig.json` files.
|
|
98
|
+
* Archicat rewrites relative paths to the generated config directory.
|
|
99
|
+
*/
|
|
100
|
+
readonly files?: readonly string[];
|
|
101
|
+
/**
|
|
102
|
+
* @description Unsupported. Put compiler options in the base or app tsconfig. Archicat owns generated `compilerOptions.paths` and does not support `baseUrl`.
|
|
103
|
+
*/
|
|
104
|
+
readonly compilerOptions?: {
|
|
105
|
+
readonly paths?: never;
|
|
106
|
+
readonly baseUrl?: never;
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* @description TypeScript integration options used to generate `.archicat/tsconfig.json`.
|
|
111
|
+
*/
|
|
112
|
+
interface TypeScriptConfigInput {
|
|
113
|
+
/**
|
|
114
|
+
* @description Partial TypeScript config merged into generated `.archicat/tsconfig.json`.
|
|
115
|
+
* Archicat rewrites relative paths from the project root to `.archicat`, appends generated type declarations to `include`, and injects generated aliases into `compilerOptions.paths`.
|
|
116
|
+
* `compilerOptions` is not supported here. Put compiler policy in the base tsconfig and app build overrides in the app tsconfig.
|
|
117
|
+
* @default {}
|
|
118
|
+
*/
|
|
119
|
+
readonly tsConfig?: TsConfigInput;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* @description User import aliases generated into `.archicat/tsconfig.json` together with Archicat module and library aliases.
|
|
123
|
+
* Aliases are resolved relative to the Archicat project root. Do not define TypeScript `compilerOptions.paths` manually for an Archicat project.
|
|
124
|
+
* @default {}
|
|
125
|
+
*/
|
|
126
|
+
type AliasConfig = Readonly<Record<string, string>>;
|
|
127
|
+
/**
|
|
128
|
+
* @description Module discovery and generated import alias configuration.
|
|
129
|
+
*/
|
|
130
|
+
interface ModulesConfigInput {
|
|
131
|
+
/**
|
|
132
|
+
* @description Directories or files where Archicat searches for module definition markers.
|
|
133
|
+
*/
|
|
134
|
+
readonly include?: readonly string[];
|
|
135
|
+
/**
|
|
136
|
+
* @description Base alias used for generated module imports.
|
|
137
|
+
*/
|
|
138
|
+
readonly alias?: string;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* @description Library discovery and generated import alias configuration.
|
|
142
|
+
*/
|
|
143
|
+
interface LibrariesConfigInput {
|
|
144
|
+
/**
|
|
145
|
+
* @description Directories or files where Archicat searches for library definition markers.
|
|
146
|
+
*/
|
|
147
|
+
readonly include?: readonly string[];
|
|
148
|
+
/**
|
|
149
|
+
* @description Base alias used for generated library imports.
|
|
150
|
+
*/
|
|
151
|
+
readonly alias?: string;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* @description App discovery configuration.
|
|
155
|
+
*/
|
|
156
|
+
interface AppsConfigInput {
|
|
157
|
+
/**
|
|
158
|
+
* @description Directories or files where Archicat searches for app definition markers.
|
|
159
|
+
*/
|
|
160
|
+
readonly include?: readonly string[];
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* @description User-facing Archicat root config input.
|
|
164
|
+
*/
|
|
165
|
+
interface ArchicatConfigInput {
|
|
166
|
+
/**
|
|
167
|
+
* @description Project root directory.
|
|
168
|
+
* @default '.'
|
|
169
|
+
*/
|
|
170
|
+
readonly root?: string;
|
|
171
|
+
/**
|
|
172
|
+
* @description Directory for generated mirror files, generated types, and reports.
|
|
173
|
+
* @default '.archicat'
|
|
174
|
+
*/
|
|
175
|
+
readonly outDir?: string;
|
|
176
|
+
/**
|
|
177
|
+
* @description TypeScript integration options used to generate `.archicat/tsconfig.json`.
|
|
178
|
+
* @default {}
|
|
179
|
+
*/
|
|
180
|
+
readonly typescript?: TypeScriptConfigInput;
|
|
181
|
+
/**
|
|
182
|
+
* @description User TypeScript aliases generated by Archicat.
|
|
183
|
+
* @default {}
|
|
184
|
+
*/
|
|
185
|
+
readonly alias?: AliasConfig;
|
|
186
|
+
/**
|
|
187
|
+
* @description Module discovery and alias config.
|
|
188
|
+
* @default { include: ['./src/modules'], alias: '#modules' }
|
|
189
|
+
*/
|
|
190
|
+
readonly modules?: ModulesConfigInput;
|
|
191
|
+
/**
|
|
192
|
+
* @description Library discovery and alias config.
|
|
193
|
+
* @default { include: [], alias: '#library' }
|
|
194
|
+
*/
|
|
195
|
+
readonly libraries?: LibrariesConfigInput;
|
|
196
|
+
/**
|
|
197
|
+
* @description App discovery config.
|
|
198
|
+
* @default { include: [] }
|
|
199
|
+
*/
|
|
200
|
+
readonly apps?: AppsConfigInput;
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* @description Immutable Archicat config returned by `defineArchicatConfig`.
|
|
204
|
+
*/
|
|
205
|
+
type ArchicatConfig = Readonly<ArchicatConfigInput>;
|
|
206
|
+
//#endregion
|
|
207
|
+
//#region src/configs/surface-config.d.ts
|
|
208
|
+
/**
|
|
209
|
+
* @description Surface root shorthand or full surface config.
|
|
210
|
+
*/
|
|
211
|
+
type ArchicatSurfaceInput<Dependency extends string> = string | ArchicatSurfaceConfig<Dependency>;
|
|
212
|
+
/**
|
|
213
|
+
* @description Source surface config.
|
|
214
|
+
*/
|
|
215
|
+
interface ArchicatSurfaceConfig<Dependency extends string> {
|
|
216
|
+
/**
|
|
217
|
+
* @description Surface source root, relative to the definition file.
|
|
218
|
+
* @default Generates an empty mirror for the surface.
|
|
219
|
+
*/
|
|
220
|
+
readonly root?: string;
|
|
221
|
+
/**
|
|
222
|
+
* @description Dependency targets visible from this surface.
|
|
223
|
+
* @default []
|
|
224
|
+
*/
|
|
225
|
+
readonly dependencies?: readonly Dependency[];
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* @description Immutable normalized surface contract.
|
|
229
|
+
*/
|
|
230
|
+
type ArchicatSurfaceContract<Dependency extends string> = Readonly<{
|
|
231
|
+
readonly root?: string;
|
|
232
|
+
readonly dependencies: readonly Dependency[];
|
|
233
|
+
}>;
|
|
234
|
+
//#endregion
|
|
235
|
+
//#region src/configs/library-config.d.ts
|
|
236
|
+
/**
|
|
237
|
+
* @description User-facing library definition input.
|
|
238
|
+
*/
|
|
239
|
+
interface ArchicatLibraryInput {
|
|
240
|
+
/**
|
|
241
|
+
* @description Stable library name used in the Archicat project graph.
|
|
242
|
+
*/
|
|
243
|
+
readonly name: string;
|
|
244
|
+
/**
|
|
245
|
+
* @description Library public API surface.
|
|
246
|
+
* @default Generates an empty public API mirror.
|
|
247
|
+
*/
|
|
248
|
+
readonly api?: ArchicatSurfaceInput<ArchicatLibraryApiDependency>;
|
|
249
|
+
/**
|
|
250
|
+
* @description Library implementation surface.
|
|
251
|
+
* @default Generates a no-op implementation mirror.
|
|
252
|
+
*/
|
|
253
|
+
readonly impl?: ArchicatSurfaceInput<ArchicatLibraryImplDependency>;
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* @description Immutable library definition contract loaded by Archicat.
|
|
257
|
+
*/
|
|
258
|
+
type ArchicatLibraryContract = Readonly<{
|
|
259
|
+
readonly kind: 'library';
|
|
260
|
+
readonly name: string;
|
|
261
|
+
readonly api: ArchicatSurfaceContract<ArchicatLibraryApiDependency>;
|
|
262
|
+
readonly impl: ArchicatSurfaceContract<ArchicatLibraryImplDependency>;
|
|
263
|
+
}>;
|
|
264
|
+
//#endregion
|
|
265
|
+
//#region src/configs/module-config.d.ts
|
|
266
|
+
/**
|
|
267
|
+
* @description User-facing module definition input.
|
|
268
|
+
*/
|
|
269
|
+
interface ArchicatModuleInput {
|
|
270
|
+
/**
|
|
271
|
+
* @description Stable module name used in the Archicat project graph.
|
|
272
|
+
*/
|
|
273
|
+
readonly name: string;
|
|
274
|
+
/**
|
|
275
|
+
* @description Module public API surface.
|
|
276
|
+
* @default Generates an empty public API mirror.
|
|
277
|
+
*/
|
|
278
|
+
readonly api?: ArchicatSurfaceInput<ArchicatModuleApiDependency>;
|
|
279
|
+
/**
|
|
280
|
+
* @description Module implementation surface.
|
|
281
|
+
* @default Generates a no-op implementation mirror.
|
|
282
|
+
*/
|
|
283
|
+
readonly impl?: ArchicatSurfaceInput<ArchicatModuleImplDependency>;
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* @description Immutable module definition contract loaded by Archicat.
|
|
287
|
+
*/
|
|
288
|
+
type ArchicatModuleContract = Readonly<{
|
|
289
|
+
readonly kind: 'module';
|
|
290
|
+
readonly name: string;
|
|
291
|
+
readonly api: ArchicatSurfaceContract<ArchicatModuleApiDependency>;
|
|
292
|
+
readonly impl: ArchicatSurfaceContract<ArchicatModuleImplDependency>;
|
|
293
|
+
}>;
|
|
294
|
+
//#endregion
|
|
295
|
+
//#region src/configs/define-app-config.d.ts
|
|
296
|
+
/**
|
|
297
|
+
* @description Defines one Archicat app composition root.
|
|
298
|
+
*/
|
|
299
|
+
declare function defineApp(app: ArchicatAppInput): ArchicatAppContract;
|
|
300
|
+
//#endregion
|
|
301
|
+
//#region src/configs/define-archicat-config.d.ts
|
|
302
|
+
/**
|
|
303
|
+
* @description Defines the root Archicat config.
|
|
304
|
+
*/
|
|
305
|
+
declare function defineArchicatConfig(config?: ArchicatConfigInput): ArchicatConfig;
|
|
306
|
+
//#endregion
|
|
307
|
+
//#region src/configs/define-library-config.d.ts
|
|
308
|
+
/**
|
|
309
|
+
* @description Defines one Archicat library.
|
|
310
|
+
*/
|
|
311
|
+
declare function defineLibrary(library: ArchicatLibraryInput): ArchicatLibraryContract;
|
|
312
|
+
//#endregion
|
|
313
|
+
//#region src/configs/define-module-config.d.ts
|
|
314
|
+
/**
|
|
315
|
+
* @description Defines one Archicat module.
|
|
316
|
+
*/
|
|
317
|
+
declare function defineModule(module: ArchicatModuleInput): ArchicatModuleContract;
|
|
318
|
+
//#endregion
|
|
319
|
+
export { type AliasConfig, type AppsConfigInput, type ArchicatAppContract, type ArchicatAppDependencies, type ArchicatAppDependency, type ArchicatAppInput, type ArchicatConfig, type ArchicatConfigInput, type ArchicatLibraryApiDependencies, type ArchicatLibraryApiDependency, type ArchicatLibraryContract, type ArchicatLibraryImplDependencies, type ArchicatLibraryImplDependency, type ArchicatLibraryInput, type ArchicatModuleApiDependencies, type ArchicatModuleApiDependency, type ArchicatModuleContract, type ArchicatModuleImplDependencies, type ArchicatModuleImplDependency, type ArchicatModuleInput, type ArchicatSurfaceConfig, type ArchicatSurfaceContract, type ArchicatSurfaceInput, type LibrariesConfigInput, type ModulesConfigInput, type TsConfigInput, type TypeScriptConfigInput, defineApp, defineArchicatConfig, defineLibrary, defineModule };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function e(e){return Object.freeze({kind:`app`,name:e.name,...e.root===void 0?{}:{root:e.root},dependencies:Object.freeze([...e.dependencies??[]])})}function t(e){let t={...e};for(let e of Object.keys(t))t[e]===void 0&&delete t[e];return Object.freeze(t)}function n(e={}){return t({root:e.root,outDir:e.outDir,typescript:e.typescript?r(e.typescript):void 0,alias:e.alias?Object.freeze({...e.alias}):void 0,modules:e.modules?a(e.modules):void 0,libraries:e.libraries?o(e.libraries):void 0,apps:e.apps?s(e.apps):void 0})}function r(e){return t({tsConfig:e.tsConfig?i(e.tsConfig):void 0})}function i(e){return t({extends:e.extends,include:e.include?Object.freeze([...e.include]):void 0,exclude:e.exclude?Object.freeze([...e.exclude]):void 0,files:e.files?Object.freeze([...e.files]):void 0,compilerOptions:e.compilerOptions?Object.freeze({...e.compilerOptions}):void 0})}function a(e){return t({include:e.include?Object.freeze([...e.include]):void 0,alias:e.alias})}function o(e){return t({include:e.include?Object.freeze([...e.include]):void 0,alias:e.alias})}function s(e){return t({include:e.include?Object.freeze([...e.include]):void 0})}function c(e){return typeof e==`string`?Object.freeze({root:e,dependencies:Object.freeze([])}):t({root:e?.root,dependencies:Object.freeze([...e?.dependencies??[]])})}function l(e){return Object.freeze({kind:`library`,name:e.name,api:c(e.api),impl:c(e.impl)})}function u(e){return Object.freeze({kind:`module`,name:e.name,api:c(e.api),impl:c(e.impl)})}export{e as defineApp,n as defineArchicatConfig,l as defineLibrary,u as defineModule};
|
package/package.json
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "archicat",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Modular mirroring (M²) for clean TypeScript architecture.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"architecture",
|
|
7
|
+
"clean-architecture",
|
|
8
|
+
"mirroring",
|
|
9
|
+
"modular",
|
|
10
|
+
"module-system"
|
|
11
|
+
],
|
|
12
|
+
"homepage": "https://github.com/BuildPlease/ArchiCat#readme",
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/BuildPlease/ArchiCat/issues"
|
|
15
|
+
},
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "git+https://github.com/BuildPlease/ArchiCat.git"
|
|
19
|
+
},
|
|
20
|
+
"license": "MIT",
|
|
21
|
+
"author": "Simon Rastislav Kovačič",
|
|
22
|
+
"type": "module",
|
|
23
|
+
"exports": {
|
|
24
|
+
".": {
|
|
25
|
+
"import": {
|
|
26
|
+
"types": "./dist/src/index.d.mts",
|
|
27
|
+
"default": "./dist/src/index.mjs"
|
|
28
|
+
},
|
|
29
|
+
"require": {
|
|
30
|
+
"types": "./dist/src/index.d.cts",
|
|
31
|
+
"default": "./dist/src/index.cjs"
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"bin": {
|
|
36
|
+
"archicat": "./bin/archicat.mjs"
|
|
37
|
+
},
|
|
38
|
+
"files": [
|
|
39
|
+
"bin",
|
|
40
|
+
"dist"
|
|
41
|
+
],
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"@buildplease/core": "1.0.1",
|
|
44
|
+
"jiti": "2.7.0"
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"@buildplease/devkit": "1.0.1",
|
|
48
|
+
"@types/node": "26.2.0",
|
|
49
|
+
"tsdown": "0.22.14",
|
|
50
|
+
"typescript": "6.0.3",
|
|
51
|
+
"unrun": "0.3.1",
|
|
52
|
+
"vitest": "4.1.10"
|
|
53
|
+
},
|
|
54
|
+
"peerDependencies": {
|
|
55
|
+
"typescript": "6.0.3"
|
|
56
|
+
},
|
|
57
|
+
"publishConfig": {
|
|
58
|
+
"access": "public",
|
|
59
|
+
"registry": "https://registry.npmjs.org/"
|
|
60
|
+
},
|
|
61
|
+
"scripts": {
|
|
62
|
+
"build": "pnpm run build:cli && pnpm run build:src",
|
|
63
|
+
"build:cli": "tsdown --config tsdown.config.cli.ts --config-loader unrun",
|
|
64
|
+
"build:src": "tsdown --config tsdown.config.src.ts --config-loader unrun",
|
|
65
|
+
"clean": "devkit run clean",
|
|
66
|
+
"format": "devkit run format-fix && devkit run lint-fix",
|
|
67
|
+
"test": "vitest run",
|
|
68
|
+
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
69
|
+
}
|
|
70
|
+
}
|