@koobiq/components 21.0.0 → 21.0.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/fesm2022/koobiq-components-code-block.mjs +45 -17
- package/fesm2022/koobiq-components-code-block.mjs.map +1 -1
- package/fesm2022/koobiq-components-core.mjs +1 -1
- package/fesm2022/koobiq-components-core.mjs.map +1 -1
- package/fesm2022/koobiq-components-filter-bar.mjs +9 -5
- package/fesm2022/koobiq-components-filter-bar.mjs.map +1 -1
- package/fesm2022/koobiq-components-icon.mjs +37 -16
- package/fesm2022/koobiq-components-icon.mjs.map +1 -1
- package/fesm2022/koobiq-components-tree-select.mjs +20 -5
- package/fesm2022/koobiq-components-tree-select.mjs.map +1 -1
- package/fesm2022/koobiq-components-tree.mjs +218 -83
- package/fesm2022/koobiq-components-tree.mjs.map +1 -1
- package/package.json +3 -3
- package/schematics/collection.json +94 -89
- package/schematics/migrations/accordion-state-saving-default/{index.js → index.cjs} +5 -4
- package/schematics/migrations/actions-panel-overlay-container/{index.js → index.cjs} +5 -4
- package/schematics/migrations/alert-signals/{index.js → index.cjs} +14 -13
- package/schematics/migrations/angular-animations-removal/index.cjs +21 -0
- package/schematics/migrations/app-switcher-signals/{index.js → index.cjs} +7 -6
- package/schematics/migrations/autocomplete-panel-width-auto/{index.js → index.cjs} +6 -5
- package/schematics/migrations/autocomplete-signals/{index.js → index.cjs} +7 -6
- package/schematics/migrations/badge-signals/{index.js → index.cjs} +7 -6
- package/schematics/migrations/button-state-and-styles/{index.js → index.cjs} +7 -6
- package/schematics/migrations/button-supported-colors/{index.js → index.cjs} +7 -6
- package/schematics/migrations/button-toggle-signals-and-aria/{index.js → index.cjs} +7 -6
- package/schematics/migrations/button-truncation/{index.js → index.cjs} +7 -6
- package/schematics/migrations/checkbox-signals/{index.js → index.cjs} +9 -8
- package/schematics/migrations/clamped-text-disclosure/{index.js → index.cjs} +5 -4
- package/schematics/migrations/code-block-highlight-js-core/README.md +53 -0
- package/schematics/migrations/code-block-highlight-js-core/data.cjs +64 -0
- package/schematics/migrations/code-block-highlight-js-core/index.cjs +319 -0
- package/schematics/migrations/code-block-highlight-js-core/schema.json +20 -0
- package/schematics/migrations/code-block-signals/{index.js → index.cjs} +7 -6
- package/schematics/migrations/color-signals/index.cjs +20 -0
- package/schematics/migrations/core-mixins-removal/index.cjs +18 -0
- package/schematics/migrations/css-selectors/{index.js → index.cjs} +4 -3
- package/schematics/migrations/datepicker-signals/index.cjs +18 -0
- package/schematics/migrations/deprecated-icons/{index.js → index.cjs} +8 -7
- package/schematics/migrations/design-tokens-v4/{index.js → index.cjs} +3 -2
- package/schematics/migrations/divider-signals-and-aria/{index.js → index.cjs} +5 -4
- package/schematics/migrations/dl-attribute-coercion/{index.js → index.cjs} +7 -6
- package/schematics/migrations/dl-standalone-names/{index.js → index.cjs} +11 -10
- package/schematics/migrations/dropdown-demote-overlay/{index.js → index.cjs} +7 -6
- package/schematics/migrations/dropdown-signals/index.cjs +20 -0
- package/schematics/migrations/empty-state-error-color/{index.js → index.cjs} +5 -4
- package/schematics/migrations/empty-state-size-attr/{index.js → index.cjs} +6 -5
- package/schematics/migrations/file-upload-cva-and-primitives/{index.js → index.cjs} +5 -4
- package/schematics/migrations/file-upload-deprecated-outputs/{index.js → index.cjs} +5 -4
- package/schematics/migrations/filter-bar-rename-action/{index.js → index.cjs} +5 -4
- package/schematics/migrations/filter-bar-signals/{index.js → index.cjs} +16 -15
- package/schematics/migrations/filter-bar-state-saving-default/{index.js → index.cjs} +5 -4
- package/schematics/migrations/flag-inner-html/{index.js → index.cjs} +5 -4
- package/schematics/migrations/form-field-signals/{index.js → index.cjs} +18 -17
- package/schematics/migrations/icon-semantics-and-api/{data.js → data.cjs} +4 -4
- package/schematics/migrations/icon-semantics-and-api/{index.js → index.cjs} +6 -5
- package/schematics/migrations/icons-replacement/{index.js → index.cjs} +4 -3
- package/schematics/migrations/inline-edit-a11y-and-types/{index.js → index.cjs} +5 -4
- package/schematics/migrations/input-number-surface/{index.js → index.cjs} +5 -4
- package/schematics/migrations/input-signals/index.cjs +18 -0
- package/schematics/migrations/link-deprecated-path/{index.js → index.cjs} +4 -3
- package/schematics/migrations/link-signals/{index.js → index.cjs} +5 -4
- package/schematics/migrations/list-signals/index.cjs +18 -0
- package/schematics/migrations/list-tree-multiple-input/{index.js → index.cjs} +7 -6
- package/schematics/migrations/loader-overlay-signals/{index.js → index.cjs} +7 -6
- package/schematics/migrations/loader-overlay-size-attr/{index.js → index.cjs} +6 -5
- package/schematics/migrations/locale-configuration-providers/{index.js → index.cjs} +5 -4
- package/schematics/migrations/mandatory-peer-dependencies/{index.js → index.cjs} +3 -2
- package/schematics/migrations/markdown-signals/{index.js → index.cjs} +7 -6
- package/schematics/migrations/modal-dialog-semantics/{index.js → index.cjs} +5 -4
- package/schematics/migrations/modal-signals/index.cjs +20 -0
- package/schematics/migrations/navbar-signals-and-aria/{index.js → index.cjs} +7 -6
- package/schematics/migrations/new-icons-pack/{index.js → index.cjs} +8 -7
- package/schematics/migrations/notification-center-signals/{index.js → index.cjs} +5 -4
- package/schematics/migrations/option-signals/index.cjs +20 -0
- package/schematics/migrations/popover-leave-delay/{index.js → index.cjs} +5 -4
- package/schematics/migrations/progress-bar-percentage-and-aria/{index.js → index.cjs} +5 -4
- package/schematics/migrations/progress-spinner-signals/{index.js → index.cjs} +7 -6
- package/schematics/migrations/query-list-signals/index.cjs +18 -0
- package/schematics/migrations/radio-signals-and-aria/{index.js → index.cjs} +9 -8
- package/schematics/migrations/read-state-dwell-handlers/{index.js → index.cjs} +5 -4
- package/schematics/migrations/scrollbar-deprecated-path/{index.js → index.cjs} +4 -3
- package/schematics/migrations/select-signal-inputs/{index.js → index.cjs} +5 -4
- package/schematics/migrations/sidepanel-non-modal-behavior/{index.js → index.cjs} +5 -4
- package/schematics/migrations/signal-inputs-subclasses/index.cjs +18 -0
- package/schematics/migrations/split-button-optional-disabled/{index.js → index.cjs} +5 -4
- package/schematics/migrations/splitter-deprecated-path/{index.js → index.cjs} +4 -3
- package/schematics/migrations/state-saving-default/{index.js → index.cjs} +5 -4
- package/schematics/migrations/table-cell-content/{index.js → index.cjs} +5 -4
- package/schematics/migrations/tabs-signals-and-aria/{index.js → index.cjs} +5 -4
- package/schematics/migrations/tag-list-cleaner/{index.js → index.cjs} +7 -6
- package/schematics/migrations/tag-slots/{index.js → index.cjs} +6 -5
- package/schematics/migrations/tags-signals/{index.js → index.cjs} +7 -6
- package/schematics/migrations/textarea-signals/{index.js → index.cjs} +7 -6
- package/schematics/migrations/time-range-title-for/{index.js → index.cjs} +5 -4
- package/schematics/migrations/timepicker-signals/{index.js → index.cjs} +7 -6
- package/schematics/migrations/timezone-grouping-utils/{index.js → index.cjs} +5 -4
- package/schematics/migrations/title-encapsulation/{index.js → index.cjs} +5 -4
- package/schematics/migrations/toast-stack-and-defaults/{index.js → index.cjs} +5 -4
- package/schematics/migrations/tooltip-pointer-events-and-types/{index.js → index.cjs} +5 -4
- package/schematics/migrations/top-bar-container-selectors/{index.js → index.cjs} +5 -4
- package/schematics/migrations/tree-select-signals/{index.js → index.cjs} +5 -4
- package/schematics/migrations/tree-signals/{index.js → index.cjs} +5 -4
- package/schematics/migrations/tree-state-saving-default/{index.js → index.cjs} +5 -4
- package/schematics/migrations/username-pipe-injection/{index.js → index.cjs} +5 -4
- package/schematics/migrations/v20-upgrade/{data.js → data.cjs} +7 -7
- package/schematics/migrations/v20-upgrade/index.cjs +19 -0
- package/schematics/migrations/v21-upgrade/{data.js → data.cjs} +22 -22
- package/schematics/migrations/v21-upgrade/{index.js → index.cjs} +11 -10
- package/schematics/migrations/zoneless-change-detection/index.cjs +21 -0
- package/schematics/migrations.json +87 -82
- package/schematics/ng-add/{index.js → index.cjs} +12 -286
- package/schematics/utils/{angular-parsing.js → angular-parsing.cjs} +2 -2
- package/schematics/utils/{deprecated-path-migration.js → deprecated-path-migration.cjs} +4 -3
- package/schematics/utils/html-config.cjs +148 -0
- package/schematics/utils/{icon-migration.js → icon-migration.cjs} +2 -2
- package/schematics/utils/list-item-pattern.cjs +16 -0
- package/schematics/utils/package-config.cjs +80 -0
- package/schematics/utils/project-files.cjs +25 -0
- package/schematics/utils/replacement-driver.cjs +337 -0
- package/schematics/utils/safe-rule.cjs +23 -0
- package/schematics/utils/signal-members-migration.cjs +779 -0
- package/schematics/utils/tag-patterns.cjs +21 -0
- package/schematics/utils/template-ref-access.cjs +155 -0
- package/schematics/utils/{typescript.js → typescript.cjs} +1 -1
- package/schematics/utils/warn-only-migration.cjs +52 -0
- package/schematics/utils/workspace-styles.cjs +123 -0
- package/types/koobiq-components-code-block.d.ts +38 -11
- package/types/koobiq-components-filter-bar.d.ts +2 -2
- package/types/koobiq-components-tree-select.d.ts +11 -8
- package/types/koobiq-components-tree.d.ts +108 -59
- package/schematics/migrations/angular-animations-removal/index.js +0 -20
- package/schematics/migrations/color-signals/index.js +0 -19
- package/schematics/migrations/core-mixins-removal/index.js +0 -17
- package/schematics/migrations/datepicker-signals/index.js +0 -17
- package/schematics/migrations/dropdown-signals/index.js +0 -19
- package/schematics/migrations/input-signals/index.js +0 -17
- package/schematics/migrations/list-signals/index.js +0 -17
- package/schematics/migrations/modal-signals/index.js +0 -19
- package/schematics/migrations/option-signals/index.js +0 -19
- package/schematics/migrations/query-list-signals/index.js +0 -17
- package/schematics/migrations/signal-inputs-subclasses/index.js +0 -17
- package/schematics/migrations/v20-upgrade/index.js +0 -18
- package/schematics/migrations/zoneless-change-detection/index.js +0 -20
- package/schematics/utils/package-config.js +0 -13
- /package/schematics/migrations/accordion-state-saving-default/{data.js → data.cjs} +0 -0
- /package/schematics/migrations/actions-panel-overlay-container/{data.js → data.cjs} +0 -0
- /package/schematics/migrations/alert-signals/{data.js → data.cjs} +0 -0
- /package/schematics/migrations/angular-animations-removal/{data.js → data.cjs} +0 -0
- /package/schematics/migrations/app-switcher-signals/{data.js → data.cjs} +0 -0
- /package/schematics/migrations/autocomplete-signals/{data.js → data.cjs} +0 -0
- /package/schematics/migrations/badge-signals/{data.js → data.cjs} +0 -0
- /package/schematics/migrations/button-state-and-styles/{data.js → data.cjs} +0 -0
- /package/schematics/migrations/button-supported-colors/{data.js → data.cjs} +0 -0
- /package/schematics/migrations/button-toggle-signals-and-aria/{data.js → data.cjs} +0 -0
- /package/schematics/migrations/button-truncation/{data.js → data.cjs} +0 -0
- /package/schematics/migrations/checkbox-signals/{data.js → data.cjs} +0 -0
- /package/schematics/migrations/clamped-text-disclosure/{data.js → data.cjs} +0 -0
- /package/schematics/migrations/code-block-signals/{data.js → data.cjs} +0 -0
- /package/schematics/migrations/color-signals/{data.js → data.cjs} +0 -0
- /package/schematics/migrations/core-mixins-removal/{data.js → data.cjs} +0 -0
- /package/schematics/migrations/css-selectors/{data.js → data.cjs} +0 -0
- /package/schematics/migrations/datepicker-signals/{data.js → data.cjs} +0 -0
- /package/schematics/migrations/deprecated-icons/{data.js → data.cjs} +0 -0
- /package/schematics/migrations/divider-signals-and-aria/{data.js → data.cjs} +0 -0
- /package/schematics/migrations/dl-attribute-coercion/{data.js → data.cjs} +0 -0
- /package/schematics/migrations/dl-standalone-names/{data.js → data.cjs} +0 -0
- /package/schematics/migrations/dropdown-demote-overlay/{data.js → data.cjs} +0 -0
- /package/schematics/migrations/dropdown-signals/{data.js → data.cjs} +0 -0
- /package/schematics/migrations/empty-state-error-color/{data.js → data.cjs} +0 -0
- /package/schematics/migrations/file-upload-cva-and-primitives/{data.js → data.cjs} +0 -0
- /package/schematics/migrations/file-upload-deprecated-outputs/{data.js → data.cjs} +0 -0
- /package/schematics/migrations/filter-bar-rename-action/{data.js → data.cjs} +0 -0
- /package/schematics/migrations/filter-bar-signals/{data.js → data.cjs} +0 -0
- /package/schematics/migrations/filter-bar-state-saving-default/{data.js → data.cjs} +0 -0
- /package/schematics/migrations/flag-inner-html/{data.js → data.cjs} +0 -0
- /package/schematics/migrations/form-field-signals/{data.js → data.cjs} +0 -0
- /package/schematics/migrations/icons-replacement/{data.js → data.cjs} +0 -0
- /package/schematics/migrations/inline-edit-a11y-and-types/{data.js → data.cjs} +0 -0
- /package/schematics/migrations/input-number-surface/{data.js → data.cjs} +0 -0
- /package/schematics/migrations/input-signals/{data.js → data.cjs} +0 -0
- /package/schematics/migrations/link-signals/{data.js → data.cjs} +0 -0
- /package/schematics/migrations/list-signals/{data.js → data.cjs} +0 -0
- /package/schematics/migrations/list-tree-multiple-input/{data.js → data.cjs} +0 -0
- /package/schematics/migrations/loader-overlay-signals/{data.js → data.cjs} +0 -0
- /package/schematics/migrations/locale-configuration-providers/{data.js → data.cjs} +0 -0
- /package/schematics/migrations/markdown-signals/{data.js → data.cjs} +0 -0
- /package/schematics/migrations/modal-dialog-semantics/{data.js → data.cjs} +0 -0
- /package/schematics/migrations/modal-signals/{data.js → data.cjs} +0 -0
- /package/schematics/migrations/navbar-signals-and-aria/{data.js → data.cjs} +0 -0
- /package/schematics/migrations/new-icons-pack/{data.js → data.cjs} +0 -0
- /package/schematics/migrations/notification-center-signals/{data.js → data.cjs} +0 -0
- /package/schematics/migrations/option-signals/{data.js → data.cjs} +0 -0
- /package/schematics/migrations/popover-leave-delay/{data.js → data.cjs} +0 -0
- /package/schematics/migrations/progress-bar-percentage-and-aria/{data.js → data.cjs} +0 -0
- /package/schematics/migrations/progress-spinner-signals/{data.js → data.cjs} +0 -0
- /package/schematics/migrations/query-list-signals/{data.js → data.cjs} +0 -0
- /package/schematics/migrations/radio-signals-and-aria/{data.js → data.cjs} +0 -0
- /package/schematics/migrations/read-state-dwell-handlers/{data.js → data.cjs} +0 -0
- /package/schematics/migrations/select-signal-inputs/{data.js → data.cjs} +0 -0
- /package/schematics/migrations/sidepanel-non-modal-behavior/{data.js → data.cjs} +0 -0
- /package/schematics/migrations/signal-inputs-subclasses/{data.js → data.cjs} +0 -0
- /package/schematics/migrations/split-button-optional-disabled/{data.js → data.cjs} +0 -0
- /package/schematics/migrations/state-saving-default/{data.js → data.cjs} +0 -0
- /package/schematics/migrations/table-cell-content/{data.js → data.cjs} +0 -0
- /package/schematics/migrations/tabs-signals-and-aria/{data.js → data.cjs} +0 -0
- /package/schematics/migrations/tag-list-cleaner/{data.js → data.cjs} +0 -0
- /package/schematics/migrations/tags-signals/{data.js → data.cjs} +0 -0
- /package/schematics/migrations/textarea-signals/{data.js → data.cjs} +0 -0
- /package/schematics/migrations/time-range-title-for/{data.js → data.cjs} +0 -0
- /package/schematics/migrations/timepicker-signals/{data.js → data.cjs} +0 -0
- /package/schematics/migrations/timezone-grouping-utils/{data.js → data.cjs} +0 -0
- /package/schematics/migrations/title-encapsulation/{data.js → data.cjs} +0 -0
- /package/schematics/migrations/toast-stack-and-defaults/{data.js → data.cjs} +0 -0
- /package/schematics/migrations/tooltip-pointer-events-and-types/{data.js → data.cjs} +0 -0
- /package/schematics/migrations/top-bar-container-selectors/{data.js → data.cjs} +0 -0
- /package/schematics/migrations/tree-select-signals/{data.js → data.cjs} +0 -0
- /package/schematics/migrations/tree-signals/{data.js → data.cjs} +0 -0
- /package/schematics/migrations/tree-state-saving-default/{data.js → data.cjs} +0 -0
- /package/schematics/migrations/username-pipe-injection/{data.js → data.cjs} +0 -0
- /package/schematics/migrations/zoneless-change-detection/{data.js → data.cjs} +0 -0
- /package/schematics/utils/{ast.js → ast.cjs} +0 -0
- /package/schematics/utils/{messages.js → messages.cjs} +0 -0
|
@@ -7,272 +7,14 @@ var tasks = require('@angular-devkit/schematics/tasks');
|
|
|
7
7
|
var utility = require('@schematics/angular/utility');
|
|
8
8
|
var app_config = require('@schematics/angular/utility/standalone/app_config');
|
|
9
9
|
var util = require('@schematics/angular/utility/standalone/util');
|
|
10
|
-
var rxjs = require('rxjs');
|
|
11
10
|
var ts = require('@schematics/angular/third_party/github.com/Microsoft/TypeScript/lib/typescript');
|
|
12
|
-
var
|
|
13
|
-
var
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
* Index of the `<` starting a real `<body` tag — one that isn't inside an HTML comment (e.g. a
|
|
20
|
-
* pasted Google Tag Manager snippet mentioning `<body>` in its install instructions) — or `-1`.
|
|
21
|
-
* Walks the string once rather than matching `<body` with a regex, so a comment can't be mistaken
|
|
22
|
-
* for the element.
|
|
23
|
-
*/
|
|
24
|
-
function findBodyTagStart(html) {
|
|
25
|
-
const lower = html.toLowerCase();
|
|
26
|
-
let i = 0;
|
|
27
|
-
while (i < html.length) {
|
|
28
|
-
if (lower.startsWith(COMMENT_START, i)) {
|
|
29
|
-
const end = lower.indexOf(COMMENT_END, i + COMMENT_START.length);
|
|
30
|
-
i = end === -1 ? html.length : end + COMMENT_END.length;
|
|
31
|
-
continue;
|
|
32
|
-
}
|
|
33
|
-
if (lower.startsWith('<body', i) && (html[i + 5] === undefined || /[\s/>]/.test(html[i + 5]))) {
|
|
34
|
-
return i;
|
|
35
|
-
}
|
|
36
|
-
i++;
|
|
37
|
-
}
|
|
38
|
-
return -1;
|
|
39
|
-
}
|
|
40
|
-
/**
|
|
41
|
-
* Index of the `>` that closes the tag starting at `tagStart`, tracking open quotes so a `>`
|
|
42
|
-
* inside a quoted attribute value (`<body data-x="a>b">`) doesn't end the tag early. `-1` if the
|
|
43
|
-
* tag is never closed.
|
|
44
|
-
*/
|
|
45
|
-
function findTagEnd(html, tagStart) {
|
|
46
|
-
let quote = null;
|
|
47
|
-
for (let i = tagStart + 1; i < html.length; i++) {
|
|
48
|
-
const char = html[i];
|
|
49
|
-
if (quote) {
|
|
50
|
-
if (char === quote)
|
|
51
|
-
quote = null;
|
|
52
|
-
}
|
|
53
|
-
else if (char === '"' || char === "'") {
|
|
54
|
-
quote = char;
|
|
55
|
-
}
|
|
56
|
-
else if (char === '>') {
|
|
57
|
-
return i;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
return -1;
|
|
61
|
-
}
|
|
62
|
-
/**
|
|
63
|
-
* Parses the attributes of the tag whose content spans `[start, end)` (between `<body` and the
|
|
64
|
-
* closing `>`), handling double-quoted, single-quoted and unquoted values — so a single-quoted
|
|
65
|
-
* `class` attribute is found exactly like a double-quoted one, instead of only the latter matching.
|
|
66
|
-
*/
|
|
67
|
-
function parseAttrs(html, start, end) {
|
|
68
|
-
const attrs = [];
|
|
69
|
-
const isSpace = (char) => /\s/.test(char);
|
|
70
|
-
let i = start;
|
|
71
|
-
while (i < end) {
|
|
72
|
-
while (i < end && (isSpace(html[i]) || html[i] === '/'))
|
|
73
|
-
i++;
|
|
74
|
-
if (i >= end)
|
|
75
|
-
break;
|
|
76
|
-
const nameStart = i;
|
|
77
|
-
while (i < end && !isSpace(html[i]) && html[i] !== '=' && html[i] !== '/' && html[i] !== '>')
|
|
78
|
-
i++;
|
|
79
|
-
if (i === nameStart) {
|
|
80
|
-
i++;
|
|
81
|
-
continue;
|
|
82
|
-
}
|
|
83
|
-
const name = html.slice(nameStart, i);
|
|
84
|
-
while (i < end && isSpace(html[i]))
|
|
85
|
-
i++;
|
|
86
|
-
let value = '';
|
|
87
|
-
let attrEnd = i;
|
|
88
|
-
if (html[i] === '=') {
|
|
89
|
-
i++;
|
|
90
|
-
while (i < end && isSpace(html[i]))
|
|
91
|
-
i++;
|
|
92
|
-
const quote = html[i] === '"' || html[i] === "'" ? html[i] : null;
|
|
93
|
-
if (quote) {
|
|
94
|
-
i++;
|
|
95
|
-
const valueStart = i;
|
|
96
|
-
while (i < end && html[i] !== quote)
|
|
97
|
-
i++;
|
|
98
|
-
value = html.slice(valueStart, i);
|
|
99
|
-
if (i < end)
|
|
100
|
-
i++;
|
|
101
|
-
}
|
|
102
|
-
else {
|
|
103
|
-
const valueStart = i;
|
|
104
|
-
while (i < end && !isSpace(html[i]) && html[i] !== '>')
|
|
105
|
-
i++;
|
|
106
|
-
value = html.slice(valueStart, i);
|
|
107
|
-
}
|
|
108
|
-
attrEnd = i;
|
|
109
|
-
}
|
|
110
|
-
attrs.push({ name, start: nameStart, end: attrEnd, value });
|
|
111
|
-
}
|
|
112
|
-
return attrs;
|
|
113
|
-
}
|
|
114
|
-
/**
|
|
115
|
-
* Sets the Koobiq theme classes on the app's `<body>` tag.
|
|
116
|
-
*
|
|
117
|
-
* `'light'`/`'dark'` get the full `kbq-app-background kbq-{theme}` pair from the theming guide —
|
|
118
|
-
* `kbq-app-background` paints the page's own background/text color, `kbq-{theme}` picks the theme.
|
|
119
|
-
* `'auto'` only gets `kbq-app-background`: `KbqThemeService` applies `kbq-light`/`kbq-dark` to
|
|
120
|
-
* `document.body` itself at runtime, so a static class here would just go stale before the app
|
|
121
|
-
* bootstraps, or fight the service once it does.
|
|
122
|
-
*
|
|
123
|
-
* Re-running is idempotent: any class this function manages is stripped from the existing `class`
|
|
124
|
-
* value first, so switching `theme` between runs doesn't leave a stale `kbq-light` sitting next to
|
|
125
|
-
* a newly added `kbq-dark`. Any other existing classes, and their relative order, are preserved.
|
|
126
|
-
*
|
|
127
|
-
* Locates the `<body>` tag and its `class` attribute with a small hand-written scanner rather than
|
|
128
|
-
* a `<body>`/`class` regex-and-`String.replace`, so quoting style (`'`, `"`, or none), a `>` inside
|
|
129
|
-
* an unrelated attribute's value, an attribute merely named `data-class`, a `<body` mentioned inside
|
|
130
|
-
* a comment, and `$`-containing values already on the tag can't corrupt the result. Deliberately
|
|
131
|
-
* doesn't pull in a full HTML parser (e.g. `parse5`) for this: it's the only thing in this package
|
|
132
|
-
* that would need one, and this package is distributed as a dependency of `@koobiq/components`,
|
|
133
|
-
* where an extra runtime `dependencies` entry isn't worth it for one small, well-tested edit.
|
|
134
|
-
*/
|
|
135
|
-
function setKoobiqThemeBodyClass(html, theme) {
|
|
136
|
-
const tagStart = findBodyTagStart(html);
|
|
137
|
-
if (tagStart === -1)
|
|
138
|
-
return { content: html, changed: false };
|
|
139
|
-
const tagEnd = findTagEnd(html, tagStart);
|
|
140
|
-
if (tagEnd === -1)
|
|
141
|
-
return { content: html, changed: false };
|
|
142
|
-
const attrs = parseAttrs(html, tagStart + '<body'.length, tagEnd);
|
|
143
|
-
const classAttr = attrs.find((attr) => attr.name.toLowerCase() === 'class');
|
|
144
|
-
const existingClasses = classAttr ? classAttr.value.split(/\s+/).filter(Boolean) : [];
|
|
145
|
-
const keptClasses = existingClasses.filter((cls) => !KNOWN_THEME_CLASSES.includes(cls));
|
|
146
|
-
const newClasses = theme === 'auto'
|
|
147
|
-
? [...keptClasses, 'kbq-app-background']
|
|
148
|
-
: [...keptClasses, 'kbq-app-background', `kbq-${theme}`];
|
|
149
|
-
const newClassValue = newClasses.join(' ');
|
|
150
|
-
if (classAttr) {
|
|
151
|
-
if (classAttr.value === newClassValue)
|
|
152
|
-
return { content: html, changed: false };
|
|
153
|
-
const updated = html.slice(0, classAttr.start) + `class="${newClassValue}"` + html.slice(classAttr.end);
|
|
154
|
-
return { content: updated, changed: true };
|
|
155
|
-
}
|
|
156
|
-
const updated = html.slice(0, tagEnd) + ` class="${newClassValue}"` + html.slice(tagEnd);
|
|
157
|
-
return { content: updated, changed: true };
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
* Whether `project` is an application (as opposed to a library) — the same check
|
|
162
|
-
* `@schematics/angular` itself uses (see `utility/workspace.js`'s `createDefaultPath`), rather than
|
|
163
|
-
* an allowlist of builder names. A project can sit on any builder — a custom webpack wrapper, an Nx
|
|
164
|
-
* executor, one this schematic has never heard of — and still be a perfectly good application; only
|
|
165
|
-
* `projectType` reliably says so.
|
|
166
|
-
*/
|
|
167
|
-
function isApplicationProject(project) {
|
|
168
|
-
return project.extensions['projectType'] === 'application';
|
|
169
|
-
}
|
|
170
|
-
const KOOBIQ_STATIC_STYLE_ENTRIES = [
|
|
171
|
-
'node_modules/@koobiq/icons/fonts/kbq-icons.css',
|
|
172
|
-
'node_modules/@koobiq/design-tokens/web/css-tokens.css',
|
|
173
|
-
'node_modules/@koobiq/components/prebuilt-themes/theme.css'
|
|
174
|
-
];
|
|
175
|
-
const THEME_TOKENS_PATTERN = /^node_modules\/@koobiq\/design-tokens\/web\/css-tokens-(light|dark)\.css$/;
|
|
176
|
-
/**
|
|
177
|
-
* The style entries documented in the installation guide, in the order they must be loaded.
|
|
178
|
-
* `'auto'` includes both token files, since `KbqThemeService` switches between them at runtime —
|
|
179
|
-
* a static `'light'`/`'dark'` choice only needs its own.
|
|
180
|
-
*
|
|
181
|
-
* `@koobiq/design-tokens` ships code-block, scrollbar and skeleton's tokens as opt-in
|
|
182
|
-
* `component-tokens*.css` files, but `prebuilt-themes/theme.css` already carries their values,
|
|
183
|
-
* baked in at `@koobiq/components`' own build time — nothing extra to wire here for them.
|
|
184
|
-
*/
|
|
185
|
-
function getKoobiqStyleEntries(theme) {
|
|
186
|
-
const [icons, tokens, prebuiltTheme] = KOOBIQ_STATIC_STYLE_ENTRIES;
|
|
187
|
-
const themeTokens = theme === 'auto' ? [
|
|
188
|
-
'node_modules/@koobiq/design-tokens/web/css-tokens-light.css',
|
|
189
|
-
'node_modules/@koobiq/design-tokens/web/css-tokens-dark.css'
|
|
190
|
-
] : [`node_modules/@koobiq/design-tokens/web/css-tokens-${theme}.css`];
|
|
191
|
-
return [icons, tokens, ...themeTokens, prebuiltTheme];
|
|
192
|
-
}
|
|
193
|
-
/**
|
|
194
|
-
* Prepends the Koobiq style entries ahead of whatever is already in `existingStyles`, matching the
|
|
195
|
-
* order documented in installation.md, where the app's own stylesheet comes last. Re-running with
|
|
196
|
-
* the same theme is a no-op; re-running with a different theme swaps the `css-tokens-*.css`
|
|
197
|
-
* entries instead of accumulating duplicates. Non-string entries (style-budget objects) and any
|
|
198
|
-
* unrelated entries are preserved, untouched, in their original order.
|
|
199
|
-
*/
|
|
200
|
-
function mergeKoobiqStyles(existingStyles, theme) {
|
|
201
|
-
const desired = getKoobiqStyleEntries(theme);
|
|
202
|
-
const rest = existingStyles.filter((entry) => {
|
|
203
|
-
if (typeof entry !== 'string')
|
|
204
|
-
return true;
|
|
205
|
-
return (!desired.includes(entry) &&
|
|
206
|
-
!KOOBIQ_STATIC_STYLE_ENTRIES.includes(entry) &&
|
|
207
|
-
!THEME_TOKENS_PATTERN.test(entry));
|
|
208
|
-
});
|
|
209
|
-
return [...desired, ...rest];
|
|
210
|
-
}
|
|
211
|
-
/**
|
|
212
|
-
* Resolves the workspace-root-relative path of a project's index.html, mirroring the
|
|
213
|
-
* `@angular/build:application` builder's own default (`<sourceRoot>/index.html`) when the
|
|
214
|
-
* `index` option isn't set explicitly. `buildTarget` is `undefined` for a project with no `build`
|
|
215
|
-
* target at all, in which case the default is the only option available.
|
|
216
|
-
*/
|
|
217
|
-
function getIndexHtmlPath(project, buildTarget) {
|
|
218
|
-
const indexOption = buildTarget?.options?.index;
|
|
219
|
-
if (typeof indexOption === 'string') {
|
|
220
|
-
return indexOption;
|
|
221
|
-
}
|
|
222
|
-
if (indexOption && typeof indexOption === 'object' && !Array.isArray(indexOption)) {
|
|
223
|
-
const input = indexOption.input;
|
|
224
|
-
if (typeof input === 'string') {
|
|
225
|
-
return input;
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
const sourceRoot = project.sourceRoot ?? `${project.root}/src`;
|
|
229
|
-
return `${sourceRoot}/index.html`;
|
|
230
|
-
}
|
|
231
|
-
/**
|
|
232
|
-
* Whether a `build` target's `server`/`ssr` options indicate the project renders on the server —
|
|
233
|
-
* `KbqThemeCookieStore` matters there, where `KbqThemeLocalStorageStore`'s default doesn't reach
|
|
234
|
-
* the server render.
|
|
235
|
-
*/
|
|
236
|
-
function isSsrBuildTarget(buildTarget) {
|
|
237
|
-
const { server, ssr } = buildTarget.options ?? {};
|
|
238
|
-
return Boolean((typeof server === 'string' && server) || ssr === true || (ssr && typeof ssr === 'object'));
|
|
239
|
-
}
|
|
240
|
-
/**
|
|
241
|
-
* Mutates `workspace` in place: for every application project (optionally scoped to
|
|
242
|
-
* `projectName`), merges the Koobiq style entries for `theme` into its `build` target's `styles`
|
|
243
|
-
* array, when it has one. Returns per-project wiring info the caller uses to edit index.html and
|
|
244
|
-
* the project's bootstrap file afterward — for every application project, regardless of whether it
|
|
245
|
-
* had a `build` target to wire styles into, since the bootstrap file (and thus the theme providers)
|
|
246
|
-
* is resolved independently of it.
|
|
247
|
-
*
|
|
248
|
-
* Only the target literally named `build` is touched — not `test` (Karma) or `serve` — keeping the
|
|
249
|
-
* change's blast radius minimal.
|
|
250
|
-
*/
|
|
251
|
-
function applyKoobiqWorkspaceStyles(workspace, theme, projectName) {
|
|
252
|
-
const wired = [];
|
|
253
|
-
for (const [name, project] of workspace.projects) {
|
|
254
|
-
if (projectName && name !== projectName)
|
|
255
|
-
continue;
|
|
256
|
-
if (!isApplicationProject(project))
|
|
257
|
-
continue;
|
|
258
|
-
const buildTarget = project.targets.get('build');
|
|
259
|
-
let stylesWired = false;
|
|
260
|
-
if (buildTarget) {
|
|
261
|
-
const options = buildTarget.options ?? (buildTarget.options = {});
|
|
262
|
-
const existingStyles = Array.isArray(options.styles) ? options.styles : [];
|
|
263
|
-
options.styles = mergeKoobiqStyles(existingStyles, theme);
|
|
264
|
-
stylesWired = true;
|
|
265
|
-
}
|
|
266
|
-
wired.push({
|
|
267
|
-
projectName: name,
|
|
268
|
-
projectRoot: project.root,
|
|
269
|
-
indexHtmlPath: getIndexHtmlPath(project, buildTarget),
|
|
270
|
-
isSsr: buildTarget ? isSsrBuildTarget(buildTarget) : false,
|
|
271
|
-
stylesWired
|
|
272
|
-
});
|
|
273
|
-
}
|
|
274
|
-
return wired;
|
|
275
|
-
}
|
|
11
|
+
var utils_htmlConfig = require('../utils/html-config.cjs');
|
|
12
|
+
var utils_messages = require('../utils/messages.cjs');
|
|
13
|
+
var utils_packageConfig = require('../utils/package-config.cjs');
|
|
14
|
+
var utils_safeRule = require('../utils/safe-rule.cjs');
|
|
15
|
+
var utils_workspaceStyles = require('../utils/workspace-styles.cjs');
|
|
16
|
+
require('../utils/project-files.cjs');
|
|
17
|
+
require('rxjs');
|
|
276
18
|
|
|
277
19
|
function noProject(project) {
|
|
278
20
|
return `Unable to find project '${project}' in the workspace`;
|
|
@@ -388,22 +130,6 @@ function replaceNode(tree, providers, node, text) {
|
|
|
388
130
|
const end = node.getEnd();
|
|
389
131
|
tree.overwrite(providers.filePath, content.slice(0, start) + text + content.slice(end));
|
|
390
132
|
}
|
|
391
|
-
/**
|
|
392
|
-
* Runs `rule` against `tree` and swallows any error it throws, logging `fallbackMessage` instead.
|
|
393
|
-
* Only ever used for the case `resolveProviders` has already told us to expect: a bootstrap it
|
|
394
|
-
* couldn't statically analyze, where `addRootProvider` is expected to fail the same way. Any other,
|
|
395
|
-
* genuinely unexpected error is left to propagate instead of being silently turned into a warning.
|
|
396
|
-
*/
|
|
397
|
-
function safeRule(rule, fallbackMessage) {
|
|
398
|
-
return async (tree, context) => {
|
|
399
|
-
try {
|
|
400
|
-
await rxjs.firstValueFrom(schematics.callRule(rule, tree, context));
|
|
401
|
-
}
|
|
402
|
-
catch {
|
|
403
|
-
utils_messages.logMessage(context.logger, fallbackMessage);
|
|
404
|
-
}
|
|
405
|
-
};
|
|
406
|
-
}
|
|
407
133
|
/** `addRootProvider` rule inserting the `kbqThemeProvider({ ... })` call built by `themeProviderCallText`. */
|
|
408
134
|
function themeProviderRule(projectName, theme) {
|
|
409
135
|
return utility.addRootProvider(projectName, ({ code, external }) => {
|
|
@@ -458,7 +184,7 @@ function ngAdd(options) {
|
|
|
458
184
|
}
|
|
459
185
|
// Installing dependencies
|
|
460
186
|
utils_packageConfig.addPackageToPackageJson(tree, '@angular/cdk', "^21.2.14");
|
|
461
|
-
utils_packageConfig.addPackageToPackageJson(tree, '@koobiq/angular-luxon-adapter', "^21.0.
|
|
187
|
+
utils_packageConfig.addPackageToPackageJson(tree, '@koobiq/angular-luxon-adapter', "^21.0.1");
|
|
462
188
|
// `@koobiq/angular-luxon-adapter` is a wrapper: it extends `LuxonDateAdapter` from the base
|
|
463
189
|
// package and re-exports its formats, both as value imports. That makes the base a mandatory
|
|
464
190
|
// peer of the wrapper, and installing a package without its mandatory peers only works on
|
|
@@ -479,7 +205,7 @@ function ngAdd(options) {
|
|
|
479
205
|
// Wiring styles: prepend the documented style entries to every application project's
|
|
480
206
|
// `build` target (or just `project`, when given), then apply the theme class to each
|
|
481
207
|
// project's index.html.
|
|
482
|
-
const wiredProjects = applyKoobiqWorkspaceStyles(workspace, theme, project);
|
|
208
|
+
const wiredProjects = utils_workspaceStyles.applyKoobiqWorkspaceStyles(workspace, theme, project);
|
|
483
209
|
await utility.writeWorkspace(tree, workspace);
|
|
484
210
|
if (wiredProjects.length === 0) {
|
|
485
211
|
utils_messages.logMessage(context.logger, noWiredProjects());
|
|
@@ -488,7 +214,7 @@ function ngAdd(options) {
|
|
|
488
214
|
let sawSsrProject = false;
|
|
489
215
|
for (const wired of wiredProjects) {
|
|
490
216
|
if (tree.exists(wired.indexHtmlPath)) {
|
|
491
|
-
const { content, changed } = setKoobiqThemeBodyClass(tree.read(wired.indexHtmlPath).toString('utf-8'), theme);
|
|
217
|
+
const { content, changed } = utils_htmlConfig.setKoobiqThemeBodyClass(tree.read(wired.indexHtmlPath).toString('utf-8'), theme);
|
|
492
218
|
if (changed)
|
|
493
219
|
tree.overwrite(wired.indexHtmlPath, content);
|
|
494
220
|
}
|
|
@@ -523,9 +249,9 @@ function ngAdd(options) {
|
|
|
523
249
|
// bootstrap, a missing `build` target, ...) — `addRootProvider` is expected to fail
|
|
524
250
|
// for the same reason, so route it through `safeRule` and tell the user to wire the
|
|
525
251
|
// provider up by hand instead.
|
|
526
|
-
providerRules.push(safeRule(themeProviderRule(wired.projectName, theme), themeProviderFallback(wired.projectName, theme)));
|
|
252
|
+
providerRules.push(utils_safeRule.safeRule(themeProviderRule(wired.projectName, theme), themeProviderFallback(wired.projectName, theme)));
|
|
527
253
|
if (theme === 'auto') {
|
|
528
|
-
providerRules.push(safeRule(themeServiceInitializerRule(wired.projectName), themeServiceFallback(wired.projectName)));
|
|
254
|
+
providerRules.push(utils_safeRule.safeRule(themeServiceInitializerRule(wired.projectName), themeServiceFallback(wired.projectName)));
|
|
529
255
|
}
|
|
530
256
|
}
|
|
531
257
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var ts = require('typescript');
|
|
4
|
-
var utils_ast = require('./ast.
|
|
5
|
-
var utils_typescript = require('./typescript.
|
|
4
|
+
var utils_ast = require('./ast.cjs');
|
|
5
|
+
var utils_typescript = require('./typescript.cjs');
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Update separate html file
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var utils_messages = require('./messages.
|
|
4
|
-
var utils_packageConfig = require('
|
|
3
|
+
var utils_messages = require('./messages.cjs');
|
|
4
|
+
var utils_packageConfig = require('./package-config.cjs');
|
|
5
|
+
var utils_projectFiles = require('./project-files.cjs');
|
|
5
6
|
require('@angular-devkit/schematics');
|
|
6
7
|
require('@schematics/angular/utility');
|
|
7
8
|
|
|
@@ -34,7 +35,7 @@ function deprecatedPathMigration(entryPoint) {
|
|
|
34
35
|
const root = projectDefinition?.root ?? '';
|
|
35
36
|
const rootDir = root ? tree.getDir(root) : tree.root;
|
|
36
37
|
let touched = 0;
|
|
37
|
-
|
|
38
|
+
utils_projectFiles.visitProjectFiles(rootDir, (filePath, entry) => {
|
|
38
39
|
if (!filePath.endsWith(TS_EXT))
|
|
39
40
|
return;
|
|
40
41
|
const originalContent = entry?.content.toString();
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const KNOWN_THEME_CLASSES = ['kbq-app-background', 'kbq-light', 'kbq-dark'];
|
|
4
|
+
const COMMENT_START = '<!--';
|
|
5
|
+
const COMMENT_END = '-->';
|
|
6
|
+
/**
|
|
7
|
+
* Index of the `<` starting a real `<body` tag — one that isn't inside an HTML comment (e.g. a
|
|
8
|
+
* pasted Google Tag Manager snippet mentioning `<body>` in its install instructions) — or `-1`.
|
|
9
|
+
* Walks the string once rather than matching `<body` with a regex, so a comment can't be mistaken
|
|
10
|
+
* for the element.
|
|
11
|
+
*/
|
|
12
|
+
function findBodyTagStart(html) {
|
|
13
|
+
const lower = html.toLowerCase();
|
|
14
|
+
let i = 0;
|
|
15
|
+
while (i < html.length) {
|
|
16
|
+
if (lower.startsWith(COMMENT_START, i)) {
|
|
17
|
+
const end = lower.indexOf(COMMENT_END, i + COMMENT_START.length);
|
|
18
|
+
i = end === -1 ? html.length : end + COMMENT_END.length;
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
if (lower.startsWith('<body', i) && (html[i + 5] === undefined || /[\s/>]/.test(html[i + 5]))) {
|
|
22
|
+
return i;
|
|
23
|
+
}
|
|
24
|
+
i++;
|
|
25
|
+
}
|
|
26
|
+
return -1;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Index of the `>` that closes the tag starting at `tagStart`, tracking open quotes so a `>`
|
|
30
|
+
* inside a quoted attribute value (`<body data-x="a>b">`) doesn't end the tag early. `-1` if the
|
|
31
|
+
* tag is never closed.
|
|
32
|
+
*/
|
|
33
|
+
function findTagEnd(html, tagStart) {
|
|
34
|
+
let quote = null;
|
|
35
|
+
for (let i = tagStart + 1; i < html.length; i++) {
|
|
36
|
+
const char = html[i];
|
|
37
|
+
if (quote) {
|
|
38
|
+
if (char === quote)
|
|
39
|
+
quote = null;
|
|
40
|
+
}
|
|
41
|
+
else if (char === '"' || char === "'") {
|
|
42
|
+
quote = char;
|
|
43
|
+
}
|
|
44
|
+
else if (char === '>') {
|
|
45
|
+
return i;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return -1;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Parses the attributes of the tag whose content spans `[start, end)` (between `<body` and the
|
|
52
|
+
* closing `>`), handling double-quoted, single-quoted and unquoted values — so a single-quoted
|
|
53
|
+
* `class` attribute is found exactly like a double-quoted one, instead of only the latter matching.
|
|
54
|
+
*/
|
|
55
|
+
function parseAttrs(html, start, end) {
|
|
56
|
+
const attrs = [];
|
|
57
|
+
const isSpace = (char) => /\s/.test(char);
|
|
58
|
+
let i = start;
|
|
59
|
+
while (i < end) {
|
|
60
|
+
while (i < end && (isSpace(html[i]) || html[i] === '/'))
|
|
61
|
+
i++;
|
|
62
|
+
if (i >= end)
|
|
63
|
+
break;
|
|
64
|
+
const nameStart = i;
|
|
65
|
+
while (i < end && !isSpace(html[i]) && html[i] !== '=' && html[i] !== '/' && html[i] !== '>')
|
|
66
|
+
i++;
|
|
67
|
+
if (i === nameStart) {
|
|
68
|
+
i++;
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
const name = html.slice(nameStart, i);
|
|
72
|
+
while (i < end && isSpace(html[i]))
|
|
73
|
+
i++;
|
|
74
|
+
let value = '';
|
|
75
|
+
let attrEnd = i;
|
|
76
|
+
if (html[i] === '=') {
|
|
77
|
+
i++;
|
|
78
|
+
while (i < end && isSpace(html[i]))
|
|
79
|
+
i++;
|
|
80
|
+
const quote = html[i] === '"' || html[i] === "'" ? html[i] : null;
|
|
81
|
+
if (quote) {
|
|
82
|
+
i++;
|
|
83
|
+
const valueStart = i;
|
|
84
|
+
while (i < end && html[i] !== quote)
|
|
85
|
+
i++;
|
|
86
|
+
value = html.slice(valueStart, i);
|
|
87
|
+
if (i < end)
|
|
88
|
+
i++;
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
const valueStart = i;
|
|
92
|
+
while (i < end && !isSpace(html[i]) && html[i] !== '>')
|
|
93
|
+
i++;
|
|
94
|
+
value = html.slice(valueStart, i);
|
|
95
|
+
}
|
|
96
|
+
attrEnd = i;
|
|
97
|
+
}
|
|
98
|
+
attrs.push({ name, start: nameStart, end: attrEnd, value });
|
|
99
|
+
}
|
|
100
|
+
return attrs;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Sets the Koobiq theme classes on the app's `<body>` tag.
|
|
104
|
+
*
|
|
105
|
+
* `'light'`/`'dark'` get the full `kbq-app-background kbq-{theme}` pair from the theming guide —
|
|
106
|
+
* `kbq-app-background` paints the page's own background/text color, `kbq-{theme}` picks the theme.
|
|
107
|
+
* `'auto'` only gets `kbq-app-background`: `KbqThemeService` applies `kbq-light`/`kbq-dark` to
|
|
108
|
+
* `document.body` itself at runtime, so a static class here would just go stale before the app
|
|
109
|
+
* bootstraps, or fight the service once it does.
|
|
110
|
+
*
|
|
111
|
+
* Re-running is idempotent: any class this function manages is stripped from the existing `class`
|
|
112
|
+
* value first, so switching `theme` between runs doesn't leave a stale `kbq-light` sitting next to
|
|
113
|
+
* a newly added `kbq-dark`. Any other existing classes, and their relative order, are preserved.
|
|
114
|
+
*
|
|
115
|
+
* Locates the `<body>` tag and its `class` attribute with a small hand-written scanner rather than
|
|
116
|
+
* a `<body>`/`class` regex-and-`String.replace`, so quoting style (`'`, `"`, or none), a `>` inside
|
|
117
|
+
* an unrelated attribute's value, an attribute merely named `data-class`, a `<body` mentioned inside
|
|
118
|
+
* a comment, and `$`-containing values already on the tag can't corrupt the result. Deliberately
|
|
119
|
+
* doesn't pull in a full HTML parser (e.g. `parse5`) for this: it's the only thing in this package
|
|
120
|
+
* that would need one, and this package is distributed as a dependency of `@koobiq/components`,
|
|
121
|
+
* where an extra runtime `dependencies` entry isn't worth it for one small, well-tested edit.
|
|
122
|
+
*/
|
|
123
|
+
function setKoobiqThemeBodyClass(html, theme) {
|
|
124
|
+
const tagStart = findBodyTagStart(html);
|
|
125
|
+
if (tagStart === -1)
|
|
126
|
+
return { content: html, changed: false };
|
|
127
|
+
const tagEnd = findTagEnd(html, tagStart);
|
|
128
|
+
if (tagEnd === -1)
|
|
129
|
+
return { content: html, changed: false };
|
|
130
|
+
const attrs = parseAttrs(html, tagStart + '<body'.length, tagEnd);
|
|
131
|
+
const classAttr = attrs.find((attr) => attr.name.toLowerCase() === 'class');
|
|
132
|
+
const existingClasses = classAttr ? classAttr.value.split(/\s+/).filter(Boolean) : [];
|
|
133
|
+
const keptClasses = existingClasses.filter((cls) => !KNOWN_THEME_CLASSES.includes(cls));
|
|
134
|
+
const newClasses = theme === 'auto'
|
|
135
|
+
? [...keptClasses, 'kbq-app-background']
|
|
136
|
+
: [...keptClasses, 'kbq-app-background', `kbq-${theme}`];
|
|
137
|
+
const newClassValue = newClasses.join(' ');
|
|
138
|
+
if (classAttr) {
|
|
139
|
+
if (classAttr.value === newClassValue)
|
|
140
|
+
return { content: html, changed: false };
|
|
141
|
+
const updated = html.slice(0, classAttr.start) + `class="${newClassValue}"` + html.slice(classAttr.end);
|
|
142
|
+
return { content: updated, changed: true };
|
|
143
|
+
}
|
|
144
|
+
const updated = html.slice(0, tagEnd) + ` class="${newClassValue}"` + html.slice(tagEnd);
|
|
145
|
+
return { content: updated, changed: true };
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
exports.setKoobiqThemeBodyClass = setKoobiqThemeBodyClass;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var ts = require('typescript');
|
|
4
|
-
var utils_ast = require('./ast.
|
|
5
|
-
var utils_typescript = require('./typescript.
|
|
4
|
+
var utils_ast = require('./ast.cjs');
|
|
5
|
+
var utils_typescript = require('./typescript.cjs');
|
|
6
6
|
|
|
7
7
|
function escapeRegExp(value) {
|
|
8
8
|
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The source of a RegExp that matches `item` (a RegExp source) together with one of its commas when it is an element
|
|
5
|
+
* of a `[…]` or `{…}` list: the comma before it, or the one after it when it comes first. Deleting the match leaves
|
|
6
|
+
* the list well-formed and the rest of the file untouched. An inline `type` modifier goes with a list element;
|
|
7
|
+
* outside a list, `item` is matched alone.
|
|
8
|
+
*/
|
|
9
|
+
function listItemPattern(item) {
|
|
10
|
+
const element = `(?:\\btype\\s+)?(?:${item})`;
|
|
11
|
+
const withCommaBefore = `,\\s*${element}(?=\\s*[,\\]}])`;
|
|
12
|
+
const firstOrOnly = `(?<=[\\[{]\\s*)${element}(?:\\s*,\\s*|(?=\\s*[\\]}]))`;
|
|
13
|
+
return `(?:${withCommaBefore}|${firstOrOnly}|(?:${item}))`;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
exports.listItemPattern = listItemPattern;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var schematics = require('@angular-devkit/schematics');
|
|
4
|
+
var utility = require('@schematics/angular/utility');
|
|
5
|
+
var utils_messages = require('./messages.cjs');
|
|
6
|
+
var utils_projectFiles = require('./project-files.cjs');
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Sorts the keys of the given object.
|
|
10
|
+
* @returns A new object instance with sorted keys
|
|
11
|
+
*/
|
|
12
|
+
function sortObjectByKeys(obj) {
|
|
13
|
+
return Object.keys(obj)
|
|
14
|
+
.sort()
|
|
15
|
+
.reduce((result, key) => (result[key] = obj[key]) && result, {});
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Adds a package to the package.json in the given tree
|
|
19
|
+
*/
|
|
20
|
+
function addPackageToPackageJson(tree, pkg, version, devDependency = false) {
|
|
21
|
+
if (tree.exists('package.json')) {
|
|
22
|
+
const sourceText = tree.read('package.json').toString('utf-8');
|
|
23
|
+
const json = JSON.parse(sourceText);
|
|
24
|
+
const dependenciesType = devDependency ? 'devDependencies' : 'dependencies';
|
|
25
|
+
const dependencies = json[dependenciesType] || {};
|
|
26
|
+
if (!dependencies[pkg]) {
|
|
27
|
+
dependencies[pkg] = version;
|
|
28
|
+
json[dependenciesType] = sortObjectByKeys(dependencies);
|
|
29
|
+
}
|
|
30
|
+
tree.overwrite('package.json', JSON.stringify(json, null, 2));
|
|
31
|
+
}
|
|
32
|
+
return tree;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Gets the version of the specified package by looking at the package.json in the given tree
|
|
36
|
+
*/
|
|
37
|
+
function getPackageVersionFromPackageJson(tree, name, includeDevDependencies = false) {
|
|
38
|
+
if (!tree.exists('package.json')) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
const packageJson = JSON.parse(tree.read('package.json').toString('utf8'));
|
|
42
|
+
if (packageJson.dependencies && packageJson.dependencies[name]) {
|
|
43
|
+
return packageJson.dependencies[name];
|
|
44
|
+
}
|
|
45
|
+
if (includeDevDependencies && packageJson.devDependencies && packageJson.devDependencies[name]) {
|
|
46
|
+
return packageJson.devDependencies[name];
|
|
47
|
+
}
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
async function setupOptions(project, tree) {
|
|
51
|
+
if (project) {
|
|
52
|
+
const workspace = await utility.readWorkspace(tree);
|
|
53
|
+
const projectWorkspace = workspace.projects.get(project);
|
|
54
|
+
if (!projectWorkspace) {
|
|
55
|
+
throw new schematics.SchematicsException(utils_messages.noProject(project));
|
|
56
|
+
}
|
|
57
|
+
return projectWorkspace;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
async function getParsingInfo(project, tree) {
|
|
61
|
+
const tsPaths = new Set();
|
|
62
|
+
const templatePaths = new Set();
|
|
63
|
+
const projectDefinition = await setupOptions(project, tree);
|
|
64
|
+
// `ng update` passes no options at all, so without a project the whole workspace is migrated.
|
|
65
|
+
const root = projectDefinition?.root;
|
|
66
|
+
utils_projectFiles.visitProjectFiles(root ? tree.getDir(root) : tree.root, (filePath) => {
|
|
67
|
+
if (filePath.endsWith('.ts')) {
|
|
68
|
+
tsPaths.add(filePath);
|
|
69
|
+
}
|
|
70
|
+
if (filePath.endsWith('.html')) {
|
|
71
|
+
templatePaths.add(filePath);
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
return { tsPaths, templatePaths, projectDefinition };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
exports.addPackageToPackageJson = addPackageToPackageJson;
|
|
78
|
+
exports.getPackageVersionFromPackageJson = getPackageVersionFromPackageJson;
|
|
79
|
+
exports.getParsingInfo = getParsingInfo;
|
|
80
|
+
exports.setupOptions = setupOptions;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/** Installed packages, build output, the Angular CLI cache and VCS metadata: never a consumer's source. */
|
|
4
|
+
const SKIPPED_DIRECTORIES = new Set(['node_modules', 'dist', '.angular', '.git']);
|
|
5
|
+
/**
|
|
6
|
+
* Calls `visitor` for every file under `dir`, in the order of `DirEntry.visit`, without descending into the
|
|
7
|
+
* skipped directories at any depth.
|
|
8
|
+
*
|
|
9
|
+
* `DirEntry.visit` lists the whole subtree before it visits anything, so a migration that filters its paths
|
|
10
|
+
* still stats every file of `node_modules` — under `ng update`, which runs over the workspace root, that is
|
|
11
|
+
* most of the run.
|
|
12
|
+
*/
|
|
13
|
+
function visitProjectFiles(dir, visitor) {
|
|
14
|
+
for (const name of dir.subfiles) {
|
|
15
|
+
const entry = dir.file(name);
|
|
16
|
+
if (entry)
|
|
17
|
+
visitor(entry.path, entry);
|
|
18
|
+
}
|
|
19
|
+
for (const name of dir.subdirs) {
|
|
20
|
+
if (!SKIPPED_DIRECTORIES.has(name))
|
|
21
|
+
visitProjectFiles(dir.dir(name), visitor);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
exports.visitProjectFiles = visitProjectFiles;
|