@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
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var utils_listItemPattern = require('./list-item-pattern.cjs');
|
|
4
|
+
var utils_messages = require('./messages.cjs');
|
|
5
|
+
var utils_packageConfig = require('./package-config.cjs');
|
|
6
|
+
var utils_projectFiles = require('./project-files.cjs');
|
|
7
|
+
require('@angular-devkit/schematics');
|
|
8
|
+
require('@schematics/angular/utility');
|
|
9
|
+
|
|
10
|
+
/** A named import declaration: the `type` of a type-only one, its specifiers, its module. */
|
|
11
|
+
const NAMED_IMPORT = /\bimport\s*(type\s*)?\{([^}]*)\}\s*from\s*['"]([^'"]+)['"]/g;
|
|
12
|
+
/** An import specifier: an inline `type`, the imported name, its alias. */
|
|
13
|
+
const IMPORT_SPECIFIER = /^\s*(type\s+)?([\w$]+)(?:\s+as\s+([\w$]+))?\s*$/;
|
|
14
|
+
const COMMENT = /\/\/[^\n]*|\/\*[\s\S]*?\*\//g;
|
|
15
|
+
function compileReplacements(replacements) {
|
|
16
|
+
return replacements.map(({ from, unless, ...replacement }) => ({
|
|
17
|
+
...replacement,
|
|
18
|
+
from: new RegExp(from, 'g'),
|
|
19
|
+
unless: unless ? new RegExp(unless) : undefined
|
|
20
|
+
}));
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Applies `replacements` to `content` in order, and collects the imports of those that changed it, which the file
|
|
24
|
+
* must gain or lose (e.g. `booleanAttribute` from `@angular/core` after a `toBoolean(` → `booleanAttribute(`
|
|
25
|
+
* rewrite).
|
|
26
|
+
*/
|
|
27
|
+
function applyReplacements(content, replacements) {
|
|
28
|
+
let result = content;
|
|
29
|
+
const importsToEnsure = [];
|
|
30
|
+
const importsToRemove = [];
|
|
31
|
+
for (const { from, to, unless, ensureImport, removeImport } of replacements) {
|
|
32
|
+
if (unless?.test(result))
|
|
33
|
+
continue;
|
|
34
|
+
const before = result;
|
|
35
|
+
result = result.replace(from, to);
|
|
36
|
+
if (result === before)
|
|
37
|
+
continue;
|
|
38
|
+
if (ensureImport)
|
|
39
|
+
importsToEnsure.push(ensureImport);
|
|
40
|
+
if (removeImport)
|
|
41
|
+
importsToRemove.push(removeImport);
|
|
42
|
+
}
|
|
43
|
+
return { content: result, changed: result !== content, importsToEnsure, importsToRemove };
|
|
44
|
+
}
|
|
45
|
+
/** The import specifiers that bind the name `local`: whether each is type-only, the name it imports, its module. */
|
|
46
|
+
function importBindings(content, local) {
|
|
47
|
+
return [...content.matchAll(NAMED_IMPORT)].flatMap(([, typeClause, specifiers, from]) => specifiers
|
|
48
|
+
.replace(COMMENT, '')
|
|
49
|
+
.split(',')
|
|
50
|
+
.flatMap((specifier) => {
|
|
51
|
+
const [, typeSpecifier, name, alias] = IMPORT_SPECIFIER.exec(specifier) ?? [];
|
|
52
|
+
return name && (alias ?? name) === local
|
|
53
|
+
? [{ typeOnly: !!(typeClause || typeSpecifier), name, from }]
|
|
54
|
+
: [];
|
|
55
|
+
}));
|
|
56
|
+
}
|
|
57
|
+
/** Removes the type-only specifier of `symbol` from the named imports of `from`, and an import it empties. */
|
|
58
|
+
function removeTypeOnlyImport(content, symbol, from) {
|
|
59
|
+
const declaration = new RegExp(`(\\bimport\\s*(?:type\\s*)?)(\\{[^}]*\\})(\\s*from\\s*['"]${escapeRegExp(from)}['"];?[ \\t]*\\r?\\n?)`, 'g');
|
|
60
|
+
const specifier = new RegExp(utils_listItemPattern.listItemPattern(`\\b${symbol}\\b(?!\\s+as\\b)`));
|
|
61
|
+
return content.replace(declaration, (statement, open, clause, close) => {
|
|
62
|
+
const rest = clause.replace(specifier, '');
|
|
63
|
+
if (rest === clause)
|
|
64
|
+
return statement;
|
|
65
|
+
return /^\{\s*\}$/.test(rest) ? '' : `${open}${rest}${close}`;
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Idempotently makes `symbol` of `from` a value import of a TypeScript source string, as the rewrites use it as a
|
|
70
|
+
* value. A value import of the name from any module, or an import of another symbol under the name, is left alone.
|
|
71
|
+
* A type-only import of it gives way to a value one: the symbol is added to the value import clause of `from`, or
|
|
72
|
+
* in a new import line at the top of the file (after any leading block / leading comments).
|
|
73
|
+
*/
|
|
74
|
+
function ensureImport(content, symbol, from) {
|
|
75
|
+
const bindings = importBindings(content, symbol);
|
|
76
|
+
// A second binding of the name would clash with it.
|
|
77
|
+
if (bindings.some((binding) => !binding.typeOnly || binding.name !== symbol || binding.from !== from)) {
|
|
78
|
+
return content;
|
|
79
|
+
}
|
|
80
|
+
// Under a type-only import, a value use of the symbol does not compile (TS1361).
|
|
81
|
+
if (bindings.length > 0)
|
|
82
|
+
content = removeTypeOnlyImport(content, symbol, from);
|
|
83
|
+
const eol = content.includes('\r\n') ? '\r\n' : '\n';
|
|
84
|
+
// Existing value import line from the same module — extend its clause.
|
|
85
|
+
const sameModule = new RegExp(`(\\bimport\\s*\\{)([^}]*)(\\}\\s*from\\s*['"]${escapeRegExp(from)}['"])`);
|
|
86
|
+
if (sameModule.test(content)) {
|
|
87
|
+
return content.replace(sameModule, (_match, open, body, close) => `${open}${appendSpecifier(body, symbol, eol)}${close}`);
|
|
88
|
+
}
|
|
89
|
+
// Otherwise, prepend a new import. Insert after any leading comment block
|
|
90
|
+
// / shebang. The simple heuristic finds the first non-comment, non-blank
|
|
91
|
+
// line and inserts right before it.
|
|
92
|
+
const newImport = `import { ${symbol} } from '${from}';`;
|
|
93
|
+
const lines = content.split(eol);
|
|
94
|
+
let insertAt = 0;
|
|
95
|
+
let inBlockComment = false;
|
|
96
|
+
for (let i = 0; i < lines.length; i++) {
|
|
97
|
+
const trimmed = lines[i].trim();
|
|
98
|
+
if (inBlockComment) {
|
|
99
|
+
if (trimmed.endsWith('*/'))
|
|
100
|
+
inBlockComment = false;
|
|
101
|
+
insertAt = i + 1;
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
if (trimmed.startsWith('/*')) {
|
|
105
|
+
inBlockComment = !trimmed.includes('*/');
|
|
106
|
+
insertAt = i + 1;
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
if (trimmed.startsWith('//') || trimmed.length === 0) {
|
|
110
|
+
insertAt = i + 1;
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
lines.splice(insertAt, 0, newImport);
|
|
116
|
+
return lines.join(eol);
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Appends `symbol` to `body`, the specifiers of a named import clause. After a line comment that ends the clause, it
|
|
120
|
+
* goes on a line of its own, ended by `eol`: on the line of the comment it would be commented out.
|
|
121
|
+
*/
|
|
122
|
+
function appendSpecifier(body, symbol, eol) {
|
|
123
|
+
const end = body.trimEnd();
|
|
124
|
+
// Where the last specifier ends, with the comments blanked out.
|
|
125
|
+
const split = end.replace(COMMENT, (comment) => ' '.repeat(comment.length)).trimEnd().length;
|
|
126
|
+
const [code, comments] = [end.slice(0, split), end.slice(split)];
|
|
127
|
+
const specifiers = `${code}${code && !code.endsWith(',') ? ',' : ''}${comments}`;
|
|
128
|
+
if (![...comments.matchAll(COMMENT)].pop()?.[0].startsWith('//'))
|
|
129
|
+
return `${specifiers} ${symbol} `;
|
|
130
|
+
const [indent] = /^[ \t]*/.exec(end.slice(end.lastIndexOf('\n') + 1));
|
|
131
|
+
return `${specifiers}${eol}${indent}${symbol}${body.slice(end.length)}`;
|
|
132
|
+
}
|
|
133
|
+
function escapeRegExp(s) {
|
|
134
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
135
|
+
}
|
|
136
|
+
/** The length of the match of the sticky `pattern` at `index` of `content`. */
|
|
137
|
+
function matchedLength(pattern, content, index) {
|
|
138
|
+
pattern.lastIndex = index;
|
|
139
|
+
return pattern.exec(content)?.[0].length ?? 0;
|
|
140
|
+
}
|
|
141
|
+
const INDENT = /[ \t]*/y;
|
|
142
|
+
const LINE_BREAK = /\r?\n/y;
|
|
143
|
+
const BLANK_LINES = /(?:[ \t]*\r?\n)*/y;
|
|
144
|
+
/**
|
|
145
|
+
* Idempotently strips `symbol` (under any alias) from the named imports of `from`, with one of its commas, and keeps
|
|
146
|
+
* the layout of the rest of the clause. An import it empties is dropped. A re-export of `from` is left to the
|
|
147
|
+
* rewrites, and dropped once they emptied it.
|
|
148
|
+
*
|
|
149
|
+
* A dropped statement takes its line with it, and the blank lines after it only when nothing but blank lines is
|
|
150
|
+
* above it: the file neither starts with a blank line nor gains two in a row.
|
|
151
|
+
*
|
|
152
|
+
* Cleans up after rewrites that delete or rename a symbol (e.g. `toBoolean(` → `booleanAttribute(` must also remove
|
|
153
|
+
* the import of `toBoolean`, which `@koobiq/components/core` no longer exports).
|
|
154
|
+
*/
|
|
155
|
+
function removeImport(content, symbol, from) {
|
|
156
|
+
const declaration = new RegExp(`\\b(import|export)(\\s*(?:type\\s*)?)(\\{[^}]*\\})(\\s*from\\s*['"]${escapeRegExp(from)}['"];?)`, 'g');
|
|
157
|
+
const specifier = new RegExp(utils_listItemPattern.listItemPattern(`(?<!\\bas\\s+)\\b${escapeRegExp(symbol)}\\b(?:\\s+as\\s+[\\w$]+)?`), 'g');
|
|
158
|
+
let result = '';
|
|
159
|
+
let end = 0;
|
|
160
|
+
for (const match of content.matchAll(declaration)) {
|
|
161
|
+
const [statement, keyword, modifier, clause, close] = match;
|
|
162
|
+
const rest = keyword === 'import' ? clause.replace(specifier, '') : clause;
|
|
163
|
+
result += content.slice(end, match.index);
|
|
164
|
+
end = match.index + statement.length;
|
|
165
|
+
if (!/^\{[\s,]*\}$/.test(rest.replace(COMMENT, ''))) {
|
|
166
|
+
result += rest === clause ? statement : `${keyword}${modifier}${rest}${close}`;
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
// The blanks around the statement go with it, and its line break when nothing else is on its line.
|
|
170
|
+
result = result.replace(/[ \t]*$/, '');
|
|
171
|
+
end += matchedLength(INDENT, content, end);
|
|
172
|
+
if (result && !result.endsWith('\n'))
|
|
173
|
+
continue;
|
|
174
|
+
const lineBreak = matchedLength(LINE_BREAK, content, end);
|
|
175
|
+
end += lineBreak;
|
|
176
|
+
if (lineBreak && (!result.trim() || /\n[ \t]*\r?\n$/.test(result))) {
|
|
177
|
+
end += matchedLength(BLANK_LINES, content, end);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return result + content.slice(end);
|
|
181
|
+
}
|
|
182
|
+
/** A named import or re-export: its keyword, an optional `type`, its specifiers in braces, its module. */
|
|
183
|
+
const NAMED_CLAUSE = /\b(import|export)(\s*(?:type\s*)?)(\{[^}]*\})(\s*from\s*['"][^'"]+['"])/g;
|
|
184
|
+
/**
|
|
185
|
+
* Drops each repeated specifier of a named import or re-export, which TypeScript rejects (TS2300): a rename makes
|
|
186
|
+
* one when the file imported the new name as well. Of the specifiers that import a name under the same local name,
|
|
187
|
+
* the first value one stays, or else the first one; the others go with one of their commas. A clause that stands in
|
|
188
|
+
* `original` as well, so the rewrites did not change it, is left alone.
|
|
189
|
+
*/
|
|
190
|
+
function dedupeImportSpecifiers(content, original = '') {
|
|
191
|
+
return content.replace(NAMED_CLAUSE, (statement, keyword, modifier, clause, rest) => {
|
|
192
|
+
if (original.includes(statement))
|
|
193
|
+
return statement;
|
|
194
|
+
const deduped = dedupeClause(clause);
|
|
195
|
+
return deduped === clause ? statement : `${keyword}${modifier}${deduped}${rest}`;
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
/** `clause`, the specifiers of a named import in braces, without the repeated ones. */
|
|
199
|
+
function dedupeClause(clause) {
|
|
200
|
+
// Blanked out, the comments neither split the clause nor read as specifiers.
|
|
201
|
+
const code = clause.replace(COMMENT, (comment) => ' '.repeat(comment.length));
|
|
202
|
+
const specifiers = [];
|
|
203
|
+
let comma = 0;
|
|
204
|
+
for (let index = 1; index < code.length; index++) {
|
|
205
|
+
if (code[index] !== ',' && index < code.length - 1)
|
|
206
|
+
continue;
|
|
207
|
+
const text = code.slice(comma + 1, index);
|
|
208
|
+
const [, typeSpecifier, name, alias] = IMPORT_SPECIFIER.exec(text) ?? [];
|
|
209
|
+
specifiers.push({
|
|
210
|
+
// The comma before it, or the opening brace of the first one.
|
|
211
|
+
comma,
|
|
212
|
+
start: comma + 1 + text.length - text.trimStart().length,
|
|
213
|
+
end: comma + 1 + text.trimEnd().length,
|
|
214
|
+
key: name ? `${name} as ${alias ?? name}` : null,
|
|
215
|
+
typeOnly: !!typeSpecifier
|
|
216
|
+
});
|
|
217
|
+
comma = index;
|
|
218
|
+
}
|
|
219
|
+
const kept = new Map();
|
|
220
|
+
specifiers.forEach(({ key, typeOnly }, index) => {
|
|
221
|
+
if (key === null)
|
|
222
|
+
return;
|
|
223
|
+
const first = kept.get(key);
|
|
224
|
+
if (first === undefined || (specifiers[first].typeOnly && !typeOnly))
|
|
225
|
+
kept.set(key, index);
|
|
226
|
+
});
|
|
227
|
+
const dropped = specifiers.map(({ key }, index) => key !== null && kept.get(key) !== index);
|
|
228
|
+
let result = clause;
|
|
229
|
+
// From the last, so that the positions of the others hold.
|
|
230
|
+
for (let index = specifiers.length - 1; index >= 0; index--) {
|
|
231
|
+
if (!dropped[index])
|
|
232
|
+
continue;
|
|
233
|
+
const { comma: before, start, end } = specifiers[index];
|
|
234
|
+
// With the comma before it after a specifier that stays, else with the comma after it.
|
|
235
|
+
const [from, to] = dropped.slice(0, index).includes(false)
|
|
236
|
+
? [before, end]
|
|
237
|
+
: [start, specifiers[index + 1].start];
|
|
238
|
+
result = result.slice(0, from) + result.slice(to);
|
|
239
|
+
}
|
|
240
|
+
return result;
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Builds the rule of a migration that rewrites the text of the project files: each `.ts`, `.html`, `.scss` and
|
|
244
|
+
* `.css` file is reported against the warn patterns, then rewritten with the replacements of its kind, and a `.ts`
|
|
245
|
+
* file loses the import specifiers the rewrites repeated and gains and loses the imports they call for. With `fix: false`, the files that would change are logged
|
|
246
|
+
* instead of written. `migrateOutsideFiles` runs after the walk; a file it changes counts in the summary.
|
|
247
|
+
*
|
|
248
|
+
* The patterns are compiled once per run rather than per file.
|
|
249
|
+
*/
|
|
250
|
+
function replacementMigrationRule(migration, options, migrateOutsideFiles) {
|
|
251
|
+
const { label } = migration;
|
|
252
|
+
const templateReplacements = compileReplacements(migration.templateReplacements);
|
|
253
|
+
const tsReplacements = [...compileReplacements(migration.tsReplacements), ...templateReplacements];
|
|
254
|
+
const scssReplacements = compileReplacements(migration.scssReplacements);
|
|
255
|
+
const warnPatterns = migration.warnPatterns.map(({ anchor, pattern, message }) => ({
|
|
256
|
+
anchor: anchor ? new RegExp(anchor) : null,
|
|
257
|
+
pattern: new RegExp(pattern),
|
|
258
|
+
message
|
|
259
|
+
}));
|
|
260
|
+
const pickReplacements = (filePath) => {
|
|
261
|
+
if (filePath.endsWith('.ts'))
|
|
262
|
+
return tsReplacements;
|
|
263
|
+
if (filePath.endsWith('.html'))
|
|
264
|
+
return templateReplacements;
|
|
265
|
+
if (filePath.endsWith('.scss') || filePath.endsWith('.css'))
|
|
266
|
+
return scssReplacements;
|
|
267
|
+
return null;
|
|
268
|
+
};
|
|
269
|
+
return async (tree, context) => {
|
|
270
|
+
// `ng update` invokes migrations with no options at all, and migrations.json declares no schema, so the
|
|
271
|
+
// schema default never reaches us — applying the fix is the intended behaviour there.
|
|
272
|
+
const fix = options.fix ?? true;
|
|
273
|
+
const projectDefinition = await utils_packageConfig.setupOptions(options.project, tree);
|
|
274
|
+
const root = projectDefinition?.root ?? '';
|
|
275
|
+
let touched = 0;
|
|
276
|
+
utils_projectFiles.visitProjectFiles(root ? tree.getDir(root) : tree.root, (filePath, entry) => {
|
|
277
|
+
const replacements = pickReplacements(filePath);
|
|
278
|
+
if (!replacements)
|
|
279
|
+
return;
|
|
280
|
+
const originalContent = entry.content.toString();
|
|
281
|
+
if (!originalContent)
|
|
282
|
+
return;
|
|
283
|
+
for (const { anchor, pattern, message } of warnPatterns) {
|
|
284
|
+
if (anchor && !anchor.test(originalContent))
|
|
285
|
+
continue;
|
|
286
|
+
if (pattern.test(originalContent))
|
|
287
|
+
utils_messages.logMessage(context.logger, [`${label} ${filePath}`, ` ${message}`]);
|
|
288
|
+
}
|
|
289
|
+
const result = applyReplacements(originalContent, replacements);
|
|
290
|
+
if (!result.changed)
|
|
291
|
+
return;
|
|
292
|
+
const content = filePath.endsWith('.ts') ? fixImports(result, originalContent) : result.content;
|
|
293
|
+
touched++;
|
|
294
|
+
if (fix) {
|
|
295
|
+
tree.overwrite(filePath, content);
|
|
296
|
+
}
|
|
297
|
+
else {
|
|
298
|
+
utils_messages.logMessage(context.logger, [`${label} would update ${filePath} (run with --fix to apply)`]);
|
|
299
|
+
}
|
|
300
|
+
});
|
|
301
|
+
if (await migrateOutsideFiles?.(tree, context, fix))
|
|
302
|
+
touched++;
|
|
303
|
+
utils_messages.logMessage(context.logger, [
|
|
304
|
+
`${label} processed tree under "${root || '<workspace root>'}", ` +
|
|
305
|
+
`${fix ? 'updated' : 'would update'} ${touched} file(s).`
|
|
306
|
+
]);
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* The content of `result`, rewritten from `original`, with the specifiers the rewrites repeated dropped, then each of
|
|
311
|
+
* its imports removed, then ensured, once.
|
|
312
|
+
*/
|
|
313
|
+
function fixImports({ content, importsToEnsure, importsToRemove }, original) {
|
|
314
|
+
content = dedupeImportSpecifiers(content, original);
|
|
315
|
+
for (const { symbol, from } of unique(importsToRemove))
|
|
316
|
+
content = removeImport(content, symbol, from);
|
|
317
|
+
for (const { symbol, from } of unique(importsToEnsure))
|
|
318
|
+
content = ensureImport(content, symbol, from);
|
|
319
|
+
return content;
|
|
320
|
+
}
|
|
321
|
+
function unique(specifiers) {
|
|
322
|
+
const keys = new Set();
|
|
323
|
+
return specifiers.filter(({ symbol, from }) => {
|
|
324
|
+
const key = `${symbol}|${from}`;
|
|
325
|
+
if (keys.has(key))
|
|
326
|
+
return false;
|
|
327
|
+
keys.add(key);
|
|
328
|
+
return true;
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
exports.applyReplacements = applyReplacements;
|
|
333
|
+
exports.compileReplacements = compileReplacements;
|
|
334
|
+
exports.dedupeImportSpecifiers = dedupeImportSpecifiers;
|
|
335
|
+
exports.ensureImport = ensureImport;
|
|
336
|
+
exports.removeImport = removeImport;
|
|
337
|
+
exports.replacementMigrationRule = replacementMigrationRule;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var schematics = require('@angular-devkit/schematics');
|
|
4
|
+
var rxjs = require('rxjs');
|
|
5
|
+
var utils_messages = require('./messages.cjs');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Runs `rule` against `tree` and swallows any error it throws, logging `fallbackMessage` instead. Meant for
|
|
9
|
+
* `addRootProvider`, which throws on a bootstrap it cannot analyze statically, such as a custom bootstrap or a missing
|
|
10
|
+
* `build` target. What the rule changed before it threw stays: `addRootProvider` adds its imports first.
|
|
11
|
+
*/
|
|
12
|
+
function safeRule(rule, fallbackMessage) {
|
|
13
|
+
return async (tree, context) => {
|
|
14
|
+
try {
|
|
15
|
+
await rxjs.firstValueFrom(schematics.callRule(rule, tree, context));
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
utils_messages.logMessage(context.logger, fallbackMessage);
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
exports.safeRule = safeRule;
|