accel2spa 0.0.0-stage → 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.
@@ -0,0 +1,15 @@
1
+ /** Parser for Java .properties files and Accelerator `base_<lang>.properties` bundles. */
2
+ export interface PropertyEntry {
3
+ value: string;
4
+ line: number;
5
+ }
6
+ /** Parse .properties text: comments, `=`/`:`/space separators, line continuations, unicode escapes. */
7
+ export declare function parseProperties(text: string): Map<string, PropertyEntry>;
8
+ export interface Bundle {
9
+ /** Language code (`en`, `de`, `pt_BR`) or `default` for the unsuffixed file. */
10
+ lang: string;
11
+ file: string;
12
+ entries: Map<string, PropertyEntry>;
13
+ }
14
+ /** Returns the language for a `base_<lang>.properties` file name, `default` for `base.properties`, else undefined. */
15
+ export declare function bundleLang(fileName: string): string | undefined;
@@ -0,0 +1,54 @@
1
+ /** Parser for Java .properties files and Accelerator `base_<lang>.properties` bundles. */
2
+ function unescape(s) {
3
+ return s.replace(/\\(u[0-9a-fA-F]{4}|[tnrf]|.)/g, (_m, g) => {
4
+ if (g[0] === 'u' && g.length === 5)
5
+ return String.fromCharCode(parseInt(g.slice(1), 16));
6
+ switch (g) {
7
+ case 't': return '\t';
8
+ case 'n': return '\n';
9
+ case 'r': return '\r';
10
+ case 'f': return '\f';
11
+ default: return g;
12
+ }
13
+ });
14
+ }
15
+ /** Parse .properties text: comments, `=`/`:`/space separators, line continuations, unicode escapes. */
16
+ export function parseProperties(text) {
17
+ const result = new Map();
18
+ const rawLines = text.replace(/^/, '').split(/\r?\n/);
19
+ for (let i = 0; i < rawLines.length; i++) {
20
+ const startLine = i + 1;
21
+ let line = rawLines[i].replace(/^\s+/, '');
22
+ if (!line || line.startsWith('#') || line.startsWith('!'))
23
+ continue;
24
+ // Continuation: odd number of trailing backslashes.
25
+ while (/(?:^|[^\\])(?:\\\\)*\\$/.test(line) && i + 1 < rawLines.length) {
26
+ line = line.slice(0, -1) + rawLines[++i].replace(/^\s+/, '');
27
+ }
28
+ let k = 0;
29
+ while (k < line.length) {
30
+ const ch = line[k];
31
+ if (ch === '\\') {
32
+ k += 2;
33
+ continue;
34
+ }
35
+ if (ch === '=' || ch === ':' || ch === ' ' || ch === '\t')
36
+ break;
37
+ k++;
38
+ }
39
+ const key = unescape(line.slice(0, k));
40
+ let rest = line.slice(k).replace(/^[ \t]*[=:]?[ \t]*/, '');
41
+ if (!key)
42
+ continue;
43
+ rest = unescape(rest);
44
+ result.set(key, { value: rest, line: startLine });
45
+ }
46
+ return result;
47
+ }
48
+ /** Returns the language for a `base_<lang>.properties` file name, `default` for `base.properties`, else undefined. */
49
+ export function bundleLang(fileName) {
50
+ const m = /^base(?:_([A-Za-z]{2,3}(?:_[A-Za-z]{2})?))?\.properties$/.exec(fileName);
51
+ if (!m)
52
+ return undefined;
53
+ return m[1] ?? 'default';
54
+ }
package/dist/util.d.ts ADDED
@@ -0,0 +1,12 @@
1
+ /** Maps character offsets to 1-based line numbers. */
2
+ export declare class LineMap {
3
+ private starts;
4
+ constructor(text: string);
5
+ lineAt(offset: number): number;
6
+ }
7
+ /** Replace every non-newline character with a space (keeps offsets and line numbers stable). */
8
+ export declare const blank: (s: string) => string;
9
+ export declare const toPosix: (p: string) => string;
10
+ export declare function kebab(s: string): string;
11
+ /** Recursively list files (posix paths relative to root). Never throws. */
12
+ export declare function walkFiles(root: string, warn: (m: string) => void): string[];
package/dist/util.js ADDED
@@ -0,0 +1,61 @@
1
+ import { readdirSync } from 'node:fs';
2
+ import path from 'node:path';
3
+ /** Maps character offsets to 1-based line numbers. */
4
+ export class LineMap {
5
+ starts = [0];
6
+ constructor(text) {
7
+ for (let i = 0; i < text.length; i++) {
8
+ if (text.charCodeAt(i) === 10)
9
+ this.starts.push(i + 1);
10
+ }
11
+ }
12
+ lineAt(offset) {
13
+ let lo = 0;
14
+ let hi = this.starts.length - 1;
15
+ while (lo < hi) {
16
+ const mid = (lo + hi + 1) >> 1;
17
+ if (this.starts[mid] <= offset)
18
+ lo = mid;
19
+ else
20
+ hi = mid - 1;
21
+ }
22
+ return lo + 1;
23
+ }
24
+ }
25
+ /** Replace every non-newline character with a space (keeps offsets and line numbers stable). */
26
+ export const blank = (s) => s.replace(/[^\r\n]/g, ' ');
27
+ export const toPosix = (p) => p.split(path.sep).join('/');
28
+ export function kebab(s) {
29
+ return s
30
+ .replace(/([a-z0-9])([A-Z])/g, '$1-$2')
31
+ .replace(/[^A-Za-z0-9]+/g, '-')
32
+ .replace(/^-+|-+$/g, '')
33
+ .toLowerCase();
34
+ }
35
+ const IGNORED_DIRS = new Set(['node_modules', 'dist', 'out']);
36
+ /** Recursively list files (posix paths relative to root). Never throws. */
37
+ export function walkFiles(root, warn) {
38
+ const result = [];
39
+ const visit = (dir) => {
40
+ let entries;
41
+ try {
42
+ entries = readdirSync(dir, { withFileTypes: true });
43
+ }
44
+ catch (e) {
45
+ warn(`Cannot read directory ${toPosix(path.relative(root, dir)) || '.'}: ${e.message}`);
46
+ return;
47
+ }
48
+ entries.sort((a, b) => a.name.localeCompare(b.name));
49
+ for (const entry of entries) {
50
+ if (entry.name.startsWith('.') || IGNORED_DIRS.has(entry.name))
51
+ continue;
52
+ const full = path.join(dir, entry.name);
53
+ if (entry.isDirectory())
54
+ visit(full);
55
+ else if (entry.isFile())
56
+ result.push(toPosix(path.relative(root, full)));
57
+ }
58
+ };
59
+ visit(root);
60
+ return result;
61
+ }
package/package.json CHANGED
@@ -1,6 +1,50 @@
1
1
  {
2
2
  "name": "accel2spa",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.0",
4
+ "description": "Reads a SAP Commerce Accelerator storefront and writes one migration spec per page for Spartacus (Composable Storefront): conditional rules, i18n gaps, data OCC does not expose, missing endpoints.",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/andreescocard/accel2spa.git"
9
+ },
10
+ "homepage": "https://github.com/andreescocard/accel2spa#readme",
11
+ "bugs": {
12
+ "url": "https://github.com/andreescocard/accel2spa/issues"
13
+ },
14
+ "type": "module",
15
+ "main": "dist/index.js",
16
+ "types": "dist/index.d.ts",
17
+ "bin": {
18
+ "accel2spa": "dist/cli.js"
19
+ },
20
+ "files": [
21
+ "dist"
22
+ ],
23
+ "engines": {
24
+ "node": ">=20"
25
+ },
26
+ "scripts": {
27
+ "build": "tsc -p tsconfig.json && node scripts/copy-assets.mjs",
28
+ "test": "vitest run",
29
+ "visuals": "python scripts/visuals/gen.py",
30
+ "analyze:fixture": "node dist/cli.js analyze fixtures/acme-storefront --out out"
31
+ },
32
+ "keywords": [
33
+ "sap-commerce",
34
+ "spartacus",
35
+ "composable-storefront",
36
+ "hybris",
37
+ "migration",
38
+ "accelerator",
39
+ "occ",
40
+ "jsp"
41
+ ],
42
+ "dependencies": {
43
+ "yaml": "^2.9.1"
44
+ },
45
+ "devDependencies": {
46
+ "@types/node": "^26.6.6",
47
+ "typescript": "^7.0.2",
48
+ "vitest": "^5.1.0"
49
+ }
50
+ }