@wssto2/vue-core 0.11.0 → 0.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/README.md +26 -0
  3. package/bin/architecture/engine.mjs +225 -0
  4. package/bin/architecture/packs.mjs +107 -0
  5. package/bin/cli.mjs +57 -0
  6. package/bin/commands/check-architecture.mjs +38 -0
  7. package/bin/commands/e2e-timings.mjs +58 -0
  8. package/bin/commands/feature.mjs +179 -0
  9. package/bin/commands/lint.mjs +532 -0
  10. package/bin/config.mjs +72 -0
  11. package/bin/templates/feature-crud/api.ts.tpl +35 -0
  12. package/bin/templates/feature-crud/components/__FEATURE_ENTITY__Modal.vue.tpl +36 -0
  13. package/bin/templates/feature-crud/form.ts.tpl +6 -0
  14. package/bin/templates/feature-crud/index.ts.tpl +7 -0
  15. package/bin/templates/feature-crud/routes.ts.tpl +13 -0
  16. package/bin/templates/feature-crud/tests/scaffold.test.ts.tpl +9 -0
  17. package/bin/templates/feature-crud/types.ts.tpl +3 -0
  18. package/bin/templates/feature-crud/views/Index.vue.tpl +36 -0
  19. package/bin/vue-core.mjs +4 -0
  20. package/dist/config/index.d.ts +97 -0
  21. package/dist/config/index.js +6 -0
  22. package/dist/eslint/index.d.ts +28 -0
  23. package/dist/eslint/index.js +87 -0
  24. package/dist/index.d.ts +1 -1
  25. package/dist/index.js +1 -1
  26. package/dist/modules/identity/entities.d.ts +3 -2
  27. package/dist/playwright/access.d.ts +61 -0
  28. package/dist/playwright/envelope.d.ts +37 -0
  29. package/dist/playwright/index.d.ts +13 -0
  30. package/dist/playwright/index.js +403 -0
  31. package/dist/playwright/notifications.d.ts +48 -0
  32. package/dist/playwright/profile.d.ts +35 -0
  33. package/dist/playwright/request.d.ts +14 -0
  34. package/dist/playwright/session.d.ts +48 -0
  35. package/dist/playwright/users.d.ts +57 -0
  36. package/package.json +48 -2
@@ -0,0 +1,179 @@
1
+ // `vue-core feature <a/b> --entity <Name>`: scaffolds a CRUD feature folder from a template folder.
2
+ // A template is a folder of `*.tpl` files in which `__FEATURE_*__` is replaced (see buildFeatureContext);
3
+ // `generator.template` points at the application's own, the default is bin/templates/feature-crud.
4
+ import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
5
+ import path from "node:path";
6
+ import { fileURLToPath } from "node:url";
7
+
8
+ export const DEFAULT_TEMPLATE_DIR = path.join(path.dirname(fileURLToPath(import.meta.url)), "..", "templates", "feature-crud");
9
+
10
+ export function parseFeatureArgs(argv) {
11
+ let featurePath = "";
12
+ let entityName = "";
13
+
14
+ for (let index = 0; index < argv.length; index += 1) {
15
+ const argument = argv[index];
16
+
17
+ if (!argument) {
18
+ continue;
19
+ }
20
+
21
+ if (!argument.startsWith("--") && !featurePath) {
22
+ featurePath = argument;
23
+ continue;
24
+ }
25
+
26
+ if (argument === "--feature") {
27
+ featurePath = argv[index + 1] ?? "";
28
+ index += 1;
29
+ continue;
30
+ }
31
+
32
+ if (argument === "--entity") {
33
+ entityName = argv[index + 1] ?? "";
34
+ index += 1;
35
+ continue;
36
+ }
37
+
38
+ throw new Error(`Unknown argument: ${argument}`);
39
+ }
40
+
41
+ if (!featurePath || !entityName) {
42
+ throw new Error("Usage: vue-core feature administration/example-reports --entity ExampleReport");
43
+ }
44
+
45
+ return { featurePath, entityName };
46
+ }
47
+
48
+ /** `featuresDirectory` is where the feature folder goes (absolute); the placeholders are the template's vocabulary. */
49
+ export function buildFeatureContext({ featurePath, entityName }, featuresDirectory = path.resolve("features")) {
50
+ const normalizedFeaturePath = normalizeFeaturePath(featurePath);
51
+ const featureSegments = normalizedFeaturePath.split("/");
52
+ const featureSlug = featureSegments.at(-1);
53
+
54
+ if (!featureSlug) {
55
+ throw new Error(`Invalid feature path: ${featurePath}`);
56
+ }
57
+
58
+ const routeGroup = featureSlug.toLowerCase().replace(/[^a-z0-9]/g, "");
59
+ const routeName = featureSegments.map(toSnakeCase).join(".");
60
+ const routePath = `/${normalizedFeaturePath}`;
61
+ const i18nModule = featureSegments.map(toSnakeCase).join(".");
62
+
63
+ return {
64
+ featurePath: normalizedFeaturePath,
65
+ featureDirectory: path.join(featuresDirectory, normalizedFeaturePath),
66
+ entityName,
67
+ FEATURE_PATH: normalizedFeaturePath,
68
+ FEATURE_ENTITY: entityName,
69
+ FEATURE_TITLE: splitPascalCase(entityName),
70
+ FEATURE_API_CLIENT: `${toCamelCase(featureSlug)}Api`,
71
+ FEATURE_LIST: `${toCamelCase(entityName)}List`,
72
+ // The route table ts:generate-routes writes for the Go feature (route.Group(<slug>, ...)).
73
+ FEATURE_ROUTE_TABLE: `${routeGroup}Routes`,
74
+ FEATURE_ROUTE_MODULE: routeGroup,
75
+ FEATURE_FORM_SCHEMA: `${entityName}FormSchema`,
76
+ FEATURE_FORM_TYPE: `${entityName}Form`,
77
+ FEATURE_META_TYPE: `${entityName}Meta`,
78
+ FEATURE_OPTIONS_TYPE: `${entityName}Options`,
79
+ FEATURE_ROUTE_NAME: `${routeName}.index`,
80
+ FEATURE_ROUTE_PATH: routePath,
81
+ FEATURE_I18N_MODULE: i18nModule,
82
+ };
83
+ }
84
+
85
+ export function generateFeatureFiles(context, templateDirectory = DEFAULT_TEMPLATE_DIR) {
86
+ return listTemplateFiles(templateDirectory).map((relativeTemplatePath) => {
87
+ const templatePath = path.join(templateDirectory, relativeTemplatePath);
88
+ const templateContent = readFileSync(templatePath, "utf8");
89
+
90
+ return {
91
+ relativePath: renderTemplate(relativeTemplatePath.replace(/\.tpl$/, ""), context), // a file may be named after the entity
92
+ content: renderTemplate(templateContent, context),
93
+ };
94
+ });
95
+ }
96
+
97
+ function listTemplateFiles(directoryPath, prefix = "") {
98
+ return readdirSync(directoryPath, { withFileTypes: true })
99
+ .flatMap((entry) => {
100
+ const relativePath = prefix ? path.join(prefix, entry.name) : entry.name;
101
+ const absolutePath = path.join(directoryPath, entry.name);
102
+
103
+ if (entry.isDirectory()) {
104
+ return listTemplateFiles(absolutePath, relativePath);
105
+ }
106
+
107
+ return [relativePath];
108
+ })
109
+ .sort();
110
+ }
111
+
112
+ function renderTemplate(template, context) {
113
+ return Object.entries(context).reduce((result, [key, value]) => {
114
+ if (typeof value !== "string") {
115
+ return result;
116
+ }
117
+
118
+ return result.replaceAll(`__${key}__`, value);
119
+ }, template);
120
+ }
121
+
122
+ function normalizeFeaturePath(featurePath) {
123
+ return featurePath
124
+ .split("/")
125
+ .map((segment) => segment.trim())
126
+ .filter(Boolean)
127
+ .join("/");
128
+ }
129
+
130
+ function toCamelCase(value) {
131
+ return value
132
+ .split(/[^A-Za-z0-9]+/)
133
+ .filter(Boolean)
134
+ .map((segment, index) => {
135
+ if (index === 0) {
136
+ return segment.charAt(0).toLowerCase() + segment.slice(1);
137
+ }
138
+
139
+ return segment.charAt(0).toUpperCase() + segment.slice(1);
140
+ })
141
+ .join("");
142
+ }
143
+
144
+ function toSnakeCase(value) {
145
+ return value
146
+ .split(/[^A-Za-z0-9]+/)
147
+ .filter(Boolean)
148
+ .map((segment) => segment.toLowerCase())
149
+ .join("_");
150
+ }
151
+
152
+ function splitPascalCase(value) {
153
+ return value.replace(/([a-z0-9])([A-Z])/g, "$1 $2");
154
+ }
155
+
156
+ /** generator.out is relative to the config's folder, generator.template likewise (the default is absolute). */
157
+ export default function run({ args, config, root, out, err }) {
158
+ try {
159
+ const context = buildFeatureContext(parseFeatureArgs(args), path.resolve(root, config.out));
160
+
161
+ if (existsSync(context.featureDirectory)) {
162
+ throw new Error(`Feature directory already exists: ${context.featureDirectory}`);
163
+ }
164
+
165
+ mkdirSync(path.join(context.featureDirectory, "components"), { recursive: true });
166
+
167
+ for (const file of generateFeatureFiles(context, path.resolve(root, config.template))) {
168
+ const outputPath = path.join(context.featureDirectory, file.relativePath);
169
+ mkdirSync(path.dirname(outputPath), { recursive: true });
170
+ writeFileSync(outputPath, file.content, "utf8");
171
+ }
172
+
173
+ out(`Created feature scaffold at ${context.featureDirectory}`);
174
+ return 0;
175
+ } catch (error) {
176
+ err(error instanceof Error ? error.message : String(error));
177
+ return 1;
178
+ }
179
+ }