@autobusal/utilities 1.3.0 → 1.4.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.
- package/CHANGELOG.md +30 -21
- package/check-backticks.mjs +91 -0
- package/package.json +1 -1
- package/passengers.ts +34 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,36 +1,45 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
All notable changes to this package are documented here. This project follows [Keep a Changelog](https://keepachangelog.com/) and [Semantic Versioning](https://semver.org/).
|
|
4
3
|
|
|
5
|
-
## 1.
|
|
4
|
+
## 1.4.1
|
|
6
5
|
|
|
7
|
-
|
|
8
|
-
One string cannot be shortened by CSS, and the seven-day strip needs to drop
|
|
9
|
-
the month on a narrow screen so seven days fit without a scroller.
|
|
6
|
+
### Changed
|
|
10
7
|
|
|
11
|
-
|
|
8
|
+
- **Gender can be "not declared".** `getOptionsSex` gains it as the first
|
|
9
|
+
option and `getSex` reports it, instead of treating everything that is not
|
|
10
|
+
`1` as male — which is what three existing rows with no value were already
|
|
11
|
+
rendering as.
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
Carried by `null`: the `visitors`.`sex` column has always been nullable, so
|
|
14
|
+
nothing migrated. The option's id is `''`, which Laravel's
|
|
15
|
+
`ConvertEmptyStringsToNull` turns into a real null on the way in, so "not
|
|
16
|
+
declared" is the *absence* of a value at every layer rather than a magic
|
|
17
|
+
number each one has to learn.
|
|
14
18
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
19
|
+
Deliberately not shared with the checkout passenger forms — those build
|
|
20
|
+
their own field, and gender there is a per-route requirement a carrier can
|
|
21
|
+
insist on. A profile is the person; a passenger record is what a carrier
|
|
22
|
+
demands in order to board them.
|
|
18
23
|
|
|
19
|
-
##
|
|
24
|
+
## 1.4.0
|
|
20
25
|
|
|
21
26
|
### Added
|
|
22
27
|
|
|
23
|
-
-
|
|
24
|
-
|
|
25
|
-
## [1.1.1] - 2026-07-19
|
|
26
|
-
|
|
27
|
-
### Added
|
|
28
|
+
- **`check-backticks.mjs`**, moved here from the autobusal repo root so the
|
|
29
|
+
apps can run it too.
|
|
28
30
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
+
A backtick used as quoting inside a styled-components template *terminates
|
|
32
|
+
the template*. The bundler then reports a syntax error somewhere further
|
|
33
|
+
down, pointing at a line that is perfectly fine — so the search starts in
|
|
34
|
+
the wrong place every time. Per the script's own history that cost five
|
|
35
|
+
build failures in one session before anyone connected the two.
|
|
31
36
|
|
|
32
|
-
|
|
37
|
+
It only ever guarded `packages`, and the trap is not confined there: it bit
|
|
38
|
+
`magus/src` the moment a public page grew its own styled-components, with
|
|
39
|
+
nothing watching. Each app's `prebuild` now runs it over its own `src`
|
|
40
|
+
(first, so a broken template stops the build before it spends minutes
|
|
41
|
+
copying files), and `publish.sh` still runs it over `packages`.
|
|
33
42
|
|
|
34
|
-
|
|
43
|
+
One script with three callers, rather than a copy per repo that drifts.
|
|
35
44
|
|
|
36
|
-
|
|
45
|
+
Takes the directory to scan as its argument.
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Refuse to publish a backtick used as quoting inside a styled-components
|
|
4
|
+
* template literal.
|
|
5
|
+
*
|
|
6
|
+
* Edited: Ferjolt Ozuni - Date: 2026-08-03
|
|
7
|
+
*
|
|
8
|
+
* A backtick inside a CSS comment TERMINATES the template string. What the
|
|
9
|
+
* bundler then reports is a syntax error somewhere further down, pointing at
|
|
10
|
+
* a line that is perfectly fine - so the search starts in the wrong place
|
|
11
|
+
* every time. It cost five build failures in a single session before anyone
|
|
12
|
+
* connected the two.
|
|
13
|
+
*
|
|
14
|
+
* TWO THINGS HERE ARE LEGITIMATE and must never be flagged, which is why
|
|
15
|
+
* this is not a plain grep:
|
|
16
|
+
*
|
|
17
|
+
* - nested css`...` interpolations, which this codebase uses everywhere
|
|
18
|
+
* - backticks in the JSDoc ABOVE a styled block, which are idiomatic and
|
|
19
|
+
* sit outside the template entirely
|
|
20
|
+
*
|
|
21
|
+
* The bug is only ever the intersection: a backtick inside a comment that
|
|
22
|
+
* sits BETWEEN styled.x` and its closing `;
|
|
23
|
+
*
|
|
24
|
+
* Edited: Ferjolt Ozuni - Date: 2026-08-06
|
|
25
|
+
* Moved into @autobusal/utilities from the repo root, so the APPS can run it
|
|
26
|
+
* too. It only ever guarded `packages`, and the trap is not confined there:
|
|
27
|
+
* it bit magus/src the moment a public page grew its own styled-components,
|
|
28
|
+
* with nothing watching. Each app's prebuild now runs it over its own src,
|
|
29
|
+
* and publish.sh still runs it over packages - one script, three callers,
|
|
30
|
+
* rather than a copy per repo that drifts.
|
|
31
|
+
*
|
|
32
|
+
* Takes the directory to scan as its argument.
|
|
33
|
+
*/
|
|
34
|
+
import { readdirSync, readFileSync, statSync } from 'node:fs';
|
|
35
|
+
import { join } from 'node:path';
|
|
36
|
+
|
|
37
|
+
const OPEN = /styled(?:\.\w+|\([^)]*\))(?:<[^>]*>)?`/g;
|
|
38
|
+
|
|
39
|
+
const walk = (dir) => readdirSync(dir).flatMap((entry) => {
|
|
40
|
+
const path = join(dir, entry);
|
|
41
|
+
|
|
42
|
+
if (entry === 'node_modules' || entry.startsWith('.')) {
|
|
43
|
+
return [];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (statSync(path).isDirectory()) {
|
|
47
|
+
return walk(path);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return /\.tsx?$/.test(entry) ? [path] : [];
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
const found = [];
|
|
54
|
+
|
|
55
|
+
for (const path of walk(process.argv[2] ?? 'packages')) {
|
|
56
|
+
const text = readFileSync(path, 'utf8');
|
|
57
|
+
|
|
58
|
+
for (const match of text.matchAll(OPEN)) {
|
|
59
|
+
const start = match.index + match[0].length;
|
|
60
|
+
const end = text.indexOf('`;', start);
|
|
61
|
+
|
|
62
|
+
if (end === -1) {
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const base = text.slice(0, start).split('\n').length;
|
|
67
|
+
let comment = false;
|
|
68
|
+
|
|
69
|
+
text.slice(start, end).split('\n').forEach((line, n) => {
|
|
70
|
+
if (line.includes('/*')) {
|
|
71
|
+
comment = true;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (comment && line.includes('`')) {
|
|
75
|
+
found.push(`${ path }:${ base + n }: ${ line.trim().slice(0, 72) }`);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (line.includes('*/')) {
|
|
79
|
+
comment = false;
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (found.length > 0) {
|
|
86
|
+
console.error('Backtick in a comment inside a styled template - this breaks the build:');
|
|
87
|
+
found.forEach((line) => console.error(` ${ line }`));
|
|
88
|
+
process.exit(1);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
console.log('backticks: clean');
|
package/package.json
CHANGED
package/passengers.ts
CHANGED
|
@@ -1,15 +1,45 @@
|
|
|
1
1
|
import { TFunction } from 'i18next';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Edited: Ferjolt Ozuni - Date: 2026-08-06
|
|
5
|
+
*
|
|
6
|
+
* Gender is OPTIONAL on a profile, and "not declared" is a real answer
|
|
7
|
+
* rather than a missing one.
|
|
8
|
+
*
|
|
9
|
+
* `null` carries it - `visitors`.`sex` has always been nullable and already
|
|
10
|
+
* held rows with no value, so nothing needed migrating. What was missing was
|
|
11
|
+
* any way to SAY it: the select offered Male and Female, the profile form
|
|
12
|
+
* defaulted to Male, and anybody who never touched the field was recorded as
|
|
13
|
+
* male. Three such rows existed already and rendered as "male" here, because
|
|
14
|
+
* this treated everything that was not 1 as male.
|
|
15
|
+
*
|
|
16
|
+
* Deliberately NOT shared with the passenger forms at checkout: those build
|
|
17
|
+
* their own field (routes-order's Gender), and gender there is a per-route
|
|
18
|
+
* requirement a carrier can insist on. A profile is the person; a passenger
|
|
19
|
+
* record is what a carrier demands in order to board them, and those are
|
|
20
|
+
* different questions.
|
|
21
|
+
*/
|
|
22
|
+
export const getSex = (value?: number | null): string => {
|
|
23
|
+
if (value === null || value === undefined) {
|
|
24
|
+
return 'undeclared';
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return value === 1 ? 'female' : 'male';
|
|
28
|
+
};
|
|
6
29
|
|
|
7
30
|
export const getOptionsSex = (t: TFunction<'common'>) => ([
|
|
8
31
|
{
|
|
32
|
+
// Empty rather than a third numeric code: Laravel's
|
|
33
|
+
// ConvertEmptyStringsToNull turns it into a real null on the way in, so
|
|
34
|
+
// "not declared" is the ABSENCE of a value at every layer rather than a
|
|
35
|
+
// magic number each of them has to learn.
|
|
36
|
+
id: '',
|
|
37
|
+
name: t('data.gender.undeclared', { ns: 'common' })
|
|
38
|
+
}, {
|
|
9
39
|
id: 0,
|
|
10
40
|
name: t('data.gender.male', { ns: 'common' })
|
|
11
41
|
}, {
|
|
12
42
|
id: 1,
|
|
13
43
|
name: t('data.gender.female', { ns: 'common' })
|
|
14
44
|
}
|
|
15
|
-
]);
|
|
45
|
+
]);
|