@rnx-kit/cli 0.14.4 → 0.14.6
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 +15 -0
- package/README.md +18 -19
- package/coverage/clover.xml +72 -71
- package/coverage/coverage-final.json +5 -5
- package/coverage/lcov-report/index.html +18 -18
- package/coverage/lcov-report/src/{bundler-plugin-defaults.ts.html → bundle/defaultPlugins.ts.html} +10 -10
- package/coverage/lcov-report/src/bundle/index.html +23 -8
- package/coverage/lcov-report/src/bundle/kit-config.ts.html +3 -3
- package/coverage/lcov-report/src/bundle/metro.ts.html +13 -22
- package/coverage/lcov-report/src/bundle/overrides.ts.html +51 -33
- package/coverage/lcov-report/src/copy-assets.ts.html +1 -1
- package/coverage/lcov-report/src/index.html +7 -22
- package/coverage/lcov-report/src/metro-config.ts.html +20 -35
- package/coverage/lcov-report/src/typescript/index.html +1 -1
- package/coverage/lcov-report/src/typescript/project-cache.ts.html +3 -3
- package/coverage/lcov.info +121 -119
- package/lib/bundle/cliOptions.d.ts +39 -0
- package/lib/bundle/cliOptions.d.ts.map +1 -0
- package/lib/bundle/cliOptions.js +75 -0
- package/lib/bundle/cliOptions.js.map +1 -0
- package/lib/{bundler-plugin-defaults.d.ts → bundle/defaultPlugins.d.ts} +1 -1
- package/lib/bundle/defaultPlugins.d.ts.map +1 -0
- package/lib/{bundler-plugin-defaults.js → bundle/defaultPlugins.js} +1 -1
- package/lib/bundle/defaultPlugins.js.map +1 -0
- package/lib/bundle/kit-config.d.ts +1 -1
- package/lib/bundle/kit-config.d.ts.map +1 -1
- package/lib/bundle/kit-config.js +3 -3
- package/lib/bundle/kit-config.js.map +1 -1
- package/lib/bundle/metro.d.ts +3 -1
- package/lib/bundle/metro.d.ts.map +1 -1
- package/lib/bundle/metro.js +29 -8
- package/lib/bundle/metro.js.map +1 -1
- package/lib/bundle/overrides.d.ts +5 -14
- package/lib/bundle/overrides.d.ts.map +1 -1
- package/lib/bundle/overrides.js +14 -13
- package/lib/bundle/overrides.js.map +1 -1
- package/lib/bundle/types.d.ts +18 -0
- package/lib/bundle/types.d.ts.map +1 -1
- package/lib/bundle.d.ts +45 -19
- package/lib/bundle.d.ts.map +1 -1
- package/lib/bundle.js +20 -3
- package/lib/bundle.js.map +1 -1
- package/lib/index.d.ts +4 -3
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +11 -6
- package/lib/index.js.map +1 -1
- package/lib/metro-config.d.ts +3 -8
- package/lib/metro-config.d.ts.map +1 -1
- package/lib/metro-config.js +2 -5
- package/lib/metro-config.js.map +1 -1
- package/lib/parsers.d.ts +1 -0
- package/lib/parsers.d.ts.map +1 -1
- package/lib/parsers.js +5 -1
- package/lib/parsers.js.map +1 -1
- package/lib/ram-bundle.d.ts +49 -0
- package/lib/ram-bundle.d.ts.map +1 -0
- package/lib/ram-bundle.js +47 -0
- package/lib/ram-bundle.js.map +1 -0
- package/lib/serve/kit-config.js +2 -2
- package/lib/serve/kit-config.js.map +1 -1
- package/lib/start.d.ts.map +1 -1
- package/lib/start.js +28 -5
- package/lib/start.js.map +1 -1
- package/lib/typescript/project-cache.js +28 -5
- package/lib/typescript/project-cache.js.map +1 -1
- package/package.json +3 -3
- package/react-native.config.js +5 -96
- package/src/bundle/cliOptions.ts +82 -0
- package/src/{bundler-plugin-defaults.ts → bundle/defaultPlugins.ts} +0 -0
- package/src/bundle/kit-config.ts +2 -2
- package/src/bundle/metro.ts +8 -11
- package/src/bundle/overrides.ts +31 -25
- package/src/bundle/types.ts +19 -0
- package/src/bundle.ts +29 -23
- package/src/index.ts +4 -3
- package/src/metro-config.ts +18 -23
- package/src/parsers.ts +4 -0
- package/src/ram-bundle.ts +78 -0
- package/src/serve/kit-config.ts +1 -1
- package/src/start.ts +5 -12
- package/src/typescript/project-cache.ts +2 -2
- package/test/bundle/overrides.test.ts +25 -14
- package/test/metro-config.test.ts +33 -45
- package/lib/bundler-plugin-defaults.d.ts.map +0 -1
- package/lib/bundler-plugin-defaults.js.map +0 -1
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import type { Config as CLIConfig } from "@react-native-community/cli-types";
|
|
2
|
+
import { info } from "@rnx-kit/console";
|
|
3
|
+
import { loadMetroConfig, ramBundle } from "@rnx-kit/metro-service";
|
|
4
|
+
import { commonBundleCommandOptions } from "./bundle/cliOptions";
|
|
5
|
+
import { getCliPlatformBundleConfigs } from "./bundle/kit-config";
|
|
6
|
+
import { metroBundle } from "./bundle/metro";
|
|
7
|
+
import {
|
|
8
|
+
applyBundleConfigOverrides,
|
|
9
|
+
overridableCommonBundleOptions,
|
|
10
|
+
} from "./bundle/overrides";
|
|
11
|
+
import type {
|
|
12
|
+
CLICommonBundleOptions,
|
|
13
|
+
CliPlatformBundleConfig,
|
|
14
|
+
} from "./bundle/types";
|
|
15
|
+
|
|
16
|
+
type CLIRamBundleOptions = CLICommonBundleOptions & {
|
|
17
|
+
indexedRamBundle?: boolean;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
function disableTreeShaking(configs: CliPlatformBundleConfig[]): void {
|
|
21
|
+
const wasEnabled = configs.reduce((modified, config) => {
|
|
22
|
+
if (config.treeShake) {
|
|
23
|
+
config.treeShake = false;
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
return modified;
|
|
27
|
+
}, false);
|
|
28
|
+
if (wasEnabled) {
|
|
29
|
+
info(
|
|
30
|
+
"`treeShake` was disabled because it is not compatible with the RAM bundle format"
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export async function rnxRamBundle(
|
|
36
|
+
_argv: Array<string>,
|
|
37
|
+
cliConfig: CLIConfig,
|
|
38
|
+
cliOptions: CLIRamBundleOptions
|
|
39
|
+
): Promise<void> {
|
|
40
|
+
const metroConfig = await loadMetroConfig(cliConfig, cliOptions);
|
|
41
|
+
|
|
42
|
+
const bundleConfigs = getCliPlatformBundleConfigs(
|
|
43
|
+
cliOptions.id,
|
|
44
|
+
cliOptions.platform
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
applyBundleConfigOverrides(cliOptions, bundleConfigs, [
|
|
48
|
+
...overridableCommonBundleOptions,
|
|
49
|
+
"indexedRamBundle",
|
|
50
|
+
]);
|
|
51
|
+
|
|
52
|
+
disableTreeShaking(bundleConfigs);
|
|
53
|
+
|
|
54
|
+
for (const bundleConfig of bundleConfigs) {
|
|
55
|
+
await metroBundle(
|
|
56
|
+
metroConfig,
|
|
57
|
+
bundleConfig,
|
|
58
|
+
cliOptions.dev,
|
|
59
|
+
cliOptions.minify,
|
|
60
|
+
ramBundle
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export const rnxRamBundleCommand = {
|
|
66
|
+
name: "rnx-ram-bundle",
|
|
67
|
+
description:
|
|
68
|
+
"Bundle your rnx-kit package in the RAM bundle format for offline use. See https://aka.ms/rnx-kit.",
|
|
69
|
+
func: rnxRamBundle,
|
|
70
|
+
options: [
|
|
71
|
+
...commonBundleCommandOptions,
|
|
72
|
+
{
|
|
73
|
+
name: "--indexed-ram-bundle",
|
|
74
|
+
description:
|
|
75
|
+
'Force the "Indexed RAM" bundle file format, even when targeting Android.',
|
|
76
|
+
},
|
|
77
|
+
],
|
|
78
|
+
};
|
package/src/serve/kit-config.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ServerConfig, BundlerPlugins } from "@rnx-kit/config";
|
|
2
2
|
import { getKitConfig, getBundleConfig } from "@rnx-kit/config";
|
|
3
|
-
import { getDefaultBundlerPlugins } from "../bundler-plugin-defaults";
|
|
4
3
|
import { pickValues } from "@rnx-kit/tools-language/properties";
|
|
4
|
+
import { getDefaultBundlerPlugins } from "../bundle/defaultPlugins";
|
|
5
5
|
|
|
6
6
|
export type ServerConfigOverrides = {
|
|
7
7
|
projectRoot?: string;
|
package/src/start.ts
CHANGED
|
@@ -11,8 +11,8 @@ import type { Server as HttpsServer } from "https";
|
|
|
11
11
|
import type { ReportableEvent, Reporter } from "metro";
|
|
12
12
|
import type { Middleware } from "metro-config";
|
|
13
13
|
import type Server from "metro/src/Server";
|
|
14
|
-
import os from "os";
|
|
15
|
-
import path from "path";
|
|
14
|
+
import * as os from "os";
|
|
15
|
+
import * as path from "path";
|
|
16
16
|
import qrcode from "qrcode";
|
|
17
17
|
import readline from "readline";
|
|
18
18
|
import { customizeMetroConfig } from "./metro-config";
|
|
@@ -137,16 +137,9 @@ export async function rnxStart(
|
|
|
137
137
|
});
|
|
138
138
|
|
|
139
139
|
// customize the metro config to include plugins, presets, etc.
|
|
140
|
-
customizeMetroConfig(
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
serverConfig.detectDuplicateDependencies,
|
|
144
|
-
serverConfig.typescriptValidation,
|
|
145
|
-
serverConfig.treeShake,
|
|
146
|
-
(message: string): void => {
|
|
147
|
-
terminal.log(message);
|
|
148
|
-
}
|
|
149
|
-
);
|
|
140
|
+
customizeMetroConfig(metroConfig, serverConfig, (message: string): void => {
|
|
141
|
+
terminal.log(message);
|
|
142
|
+
});
|
|
150
143
|
|
|
151
144
|
// create middleware -- a collection of plugins which handle incoming
|
|
152
145
|
// http(s) requests, routing them to static pages or JS functions.
|
|
@@ -9,8 +9,8 @@ import {
|
|
|
9
9
|
Project,
|
|
10
10
|
readConfigFile,
|
|
11
11
|
} from "@rnx-kit/typescript-service";
|
|
12
|
-
import fs from "fs";
|
|
13
|
-
import path from "path";
|
|
12
|
+
import * as fs from "fs";
|
|
13
|
+
import * as path from "path";
|
|
14
14
|
import ts from "typescript";
|
|
15
15
|
|
|
16
16
|
export type ProjectInfo = {
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import "jest-extended";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
applyBundleConfigOverrides,
|
|
4
|
+
overridableCommonBundleOptions,
|
|
5
|
+
} from "../../src/bundle/overrides";
|
|
3
6
|
import type { CliPlatformBundleConfig } from "../../src/bundle/types";
|
|
4
7
|
|
|
5
8
|
describe("CLI > Bundle > Overrides > applyBundleConfigOverrides", () => {
|
|
@@ -11,12 +14,17 @@ describe("CLI > Bundle > Overrides > applyBundleConfigOverrides", () => {
|
|
|
11
14
|
detectDuplicateDependencies: true,
|
|
12
15
|
typescriptValidation: true,
|
|
13
16
|
treeShake: true,
|
|
17
|
+
indexedRamBundle: false,
|
|
14
18
|
platform: "ios",
|
|
15
19
|
};
|
|
16
20
|
|
|
17
21
|
test("has no effect when no overrides are given", () => {
|
|
18
22
|
const copy = { ...config };
|
|
19
|
-
applyBundleConfigOverrides(
|
|
23
|
+
applyBundleConfigOverrides(
|
|
24
|
+
{},
|
|
25
|
+
[copy],
|
|
26
|
+
[...overridableCommonBundleOptions, "treeShake"]
|
|
27
|
+
);
|
|
20
28
|
expect(copy).toEqual(config);
|
|
21
29
|
});
|
|
22
30
|
|
|
@@ -28,10 +36,9 @@ describe("CLI > Bundle > Overrides > applyBundleConfigOverrides", () => {
|
|
|
28
36
|
}
|
|
29
37
|
}
|
|
30
38
|
applyBundleConfigOverrides(
|
|
31
|
-
{
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
[copy]
|
|
39
|
+
{ [name]: value },
|
|
40
|
+
[copy],
|
|
41
|
+
[...overridableCommonBundleOptions, "treeShake", "indexedRamBundle"]
|
|
35
42
|
);
|
|
36
43
|
expect(copy).toEqual({
|
|
37
44
|
...config,
|
|
@@ -39,35 +46,39 @@ describe("CLI > Bundle > Overrides > applyBundleConfigOverrides", () => {
|
|
|
39
46
|
});
|
|
40
47
|
}
|
|
41
48
|
|
|
42
|
-
test("changes entryFile using an override", () => {
|
|
49
|
+
test("changes `entryFile` using an override", () => {
|
|
43
50
|
testOverride("entryFile", "foo.js");
|
|
44
51
|
});
|
|
45
52
|
|
|
46
|
-
test("changes bundleOutput using an override", () => {
|
|
53
|
+
test("changes `bundleOutput` using an override", () => {
|
|
47
54
|
testOverride("bundleOutput", "foo.bundle");
|
|
48
55
|
});
|
|
49
56
|
|
|
50
|
-
test("sets bundleEncoding using an override", () => {
|
|
57
|
+
test("sets `bundleEncoding` using an override", () => {
|
|
51
58
|
testOverride("bundleEncoding", "utf8");
|
|
52
59
|
});
|
|
53
60
|
|
|
54
|
-
test("sets sourcemapOutput using an override", () => {
|
|
61
|
+
test("sets `sourcemapOutput` using an override", () => {
|
|
55
62
|
testOverride("sourcemapOutput", "out/foo.map");
|
|
56
63
|
});
|
|
57
64
|
|
|
58
|
-
test("sets sourcemapSourcesRoot using an override", () => {
|
|
65
|
+
test("sets `sourcemapSourcesRoot` using an override", () => {
|
|
59
66
|
testOverride("sourcemapSourcesRoot", "/myrepo/packags/foo");
|
|
60
67
|
});
|
|
61
68
|
|
|
62
|
-
test("changes sourcemapUseAbsolutePath using an override", () => {
|
|
69
|
+
test("changes `sourcemapUseAbsolutePath` using an override", () => {
|
|
63
70
|
testOverride("sourcemapUseAbsolutePath", true);
|
|
64
71
|
});
|
|
65
72
|
|
|
66
|
-
test("sets assetsDest using an override", () => {
|
|
73
|
+
test("sets `assetsDest` using an override", () => {
|
|
67
74
|
testOverride("assetsDest", "dist");
|
|
68
75
|
});
|
|
69
76
|
|
|
70
|
-
test("changes treeShake using an override", () => {
|
|
77
|
+
test("changes `treeShake` using an override", () => {
|
|
71
78
|
testOverride("treeShake", false);
|
|
72
79
|
});
|
|
80
|
+
|
|
81
|
+
test("changes `indexedRamBundle` using an override", () => {
|
|
82
|
+
testOverride("indexedRamBundle", true);
|
|
83
|
+
});
|
|
73
84
|
});
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { CyclicDependencies } from "@rnx-kit/metro-plugin-cyclic-dependencies-detector";
|
|
2
2
|
import { DuplicateDependencies } from "@rnx-kit/metro-plugin-duplicates-checker";
|
|
3
|
+
import type { InputConfigT } from "metro-config";
|
|
3
4
|
import { customizeMetroConfig } from "../src/metro-config";
|
|
4
5
|
|
|
5
6
|
jest.mock("@rnx-kit/metro-plugin-cyclic-dependencies-detector", () => {
|
|
@@ -14,29 +15,26 @@ jest.mock("@rnx-kit/metro-plugin-duplicates-checker", () => {
|
|
|
14
15
|
};
|
|
15
16
|
});
|
|
16
17
|
|
|
17
|
-
|
|
18
|
-
|
|
18
|
+
// Always use a new mock since `customizeMetroConfig()` modifies the object.
|
|
19
|
+
function makeMockConfig(): InputConfigT {
|
|
20
|
+
return {
|
|
19
21
|
serializer: {
|
|
20
22
|
customSerializer: false,
|
|
21
23
|
experimentalSerializerHook: false,
|
|
22
24
|
},
|
|
23
25
|
transformer: {},
|
|
24
|
-
};
|
|
26
|
+
} as unknown as InputConfigT;
|
|
27
|
+
}
|
|
25
28
|
|
|
29
|
+
describe("cli/metro-config/customizeMetroConfig", () => {
|
|
26
30
|
afterEach(() => {
|
|
27
31
|
(CyclicDependencies as any).mockClear();
|
|
28
32
|
(DuplicateDependencies as any).mockClear();
|
|
29
33
|
});
|
|
30
34
|
|
|
31
35
|
test("returns a config with plugins by default", () => {
|
|
32
|
-
const inputConfig =
|
|
33
|
-
customizeMetroConfig(
|
|
34
|
-
inputConfig as any,
|
|
35
|
-
undefined,
|
|
36
|
-
undefined,
|
|
37
|
-
undefined,
|
|
38
|
-
undefined
|
|
39
|
-
);
|
|
36
|
+
const inputConfig = makeMockConfig();
|
|
37
|
+
customizeMetroConfig(inputConfig, {});
|
|
40
38
|
|
|
41
39
|
expect(inputConfig).toEqual({
|
|
42
40
|
serializer: {
|
|
@@ -54,14 +52,11 @@ describe("cli/metro-config/customizeMetroConfig", () => {
|
|
|
54
52
|
});
|
|
55
53
|
|
|
56
54
|
test("returns a config without a custom serializer when there are no plugins", () => {
|
|
57
|
-
const inputConfig =
|
|
58
|
-
customizeMetroConfig(
|
|
59
|
-
|
|
60
|
-
false,
|
|
61
|
-
|
|
62
|
-
undefined,
|
|
63
|
-
undefined
|
|
64
|
-
);
|
|
55
|
+
const inputConfig = makeMockConfig();
|
|
56
|
+
customizeMetroConfig(inputConfig, {
|
|
57
|
+
detectCyclicDependencies: false,
|
|
58
|
+
detectDuplicateDependencies: false,
|
|
59
|
+
});
|
|
65
60
|
|
|
66
61
|
expect(inputConfig).toEqual({
|
|
67
62
|
serializer: {
|
|
@@ -78,14 +73,10 @@ describe("cli/metro-config/customizeMetroConfig", () => {
|
|
|
78
73
|
});
|
|
79
74
|
|
|
80
75
|
test("returns a config with only duplicates plugin", () => {
|
|
81
|
-
const inputConfig =
|
|
82
|
-
customizeMetroConfig(
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
undefined,
|
|
86
|
-
undefined,
|
|
87
|
-
undefined
|
|
88
|
-
);
|
|
76
|
+
const inputConfig = makeMockConfig();
|
|
77
|
+
customizeMetroConfig(inputConfig, {
|
|
78
|
+
detectCyclicDependencies: false,
|
|
79
|
+
});
|
|
89
80
|
|
|
90
81
|
expect(inputConfig).toEqual({
|
|
91
82
|
serializer: {
|
|
@@ -103,14 +94,10 @@ describe("cli/metro-config/customizeMetroConfig", () => {
|
|
|
103
94
|
});
|
|
104
95
|
|
|
105
96
|
test("returns a config with only cyclic dependencies plugin", () => {
|
|
106
|
-
const inputConfig =
|
|
107
|
-
customizeMetroConfig(
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
false,
|
|
111
|
-
undefined,
|
|
112
|
-
undefined
|
|
113
|
-
);
|
|
97
|
+
const inputConfig = makeMockConfig();
|
|
98
|
+
customizeMetroConfig(inputConfig, {
|
|
99
|
+
detectDuplicateDependencies: false,
|
|
100
|
+
});
|
|
114
101
|
|
|
115
102
|
expect(inputConfig).toEqual({
|
|
116
103
|
serializer: {
|
|
@@ -128,16 +115,13 @@ describe("cli/metro-config/customizeMetroConfig", () => {
|
|
|
128
115
|
});
|
|
129
116
|
|
|
130
117
|
test("forwards plugin options", () => {
|
|
131
|
-
const inputConfig =
|
|
118
|
+
const inputConfig = makeMockConfig();
|
|
132
119
|
const cyclicDependenciesOptions = { cyclicDependencies: true } as any;
|
|
133
120
|
const duplicateDependencesOptions = { duplicateDependencies: true } as any;
|
|
134
|
-
customizeMetroConfig(
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
undefined,
|
|
139
|
-
undefined
|
|
140
|
-
);
|
|
121
|
+
customizeMetroConfig(inputConfig, {
|
|
122
|
+
detectCyclicDependencies: cyclicDependenciesOptions,
|
|
123
|
+
detectDuplicateDependencies: duplicateDependencesOptions,
|
|
124
|
+
});
|
|
141
125
|
|
|
142
126
|
expect(inputConfig).toEqual({
|
|
143
127
|
serializer: {
|
|
@@ -157,8 +141,12 @@ describe("cli/metro-config/customizeMetroConfig", () => {
|
|
|
157
141
|
});
|
|
158
142
|
|
|
159
143
|
test("returns a config with a custom serializer when tree shaking is enabled", () => {
|
|
160
|
-
const inputConfig =
|
|
161
|
-
customizeMetroConfig(inputConfig
|
|
144
|
+
const inputConfig = makeMockConfig();
|
|
145
|
+
customizeMetroConfig(inputConfig, {
|
|
146
|
+
detectCyclicDependencies: false,
|
|
147
|
+
detectDuplicateDependencies: false,
|
|
148
|
+
treeShake: true,
|
|
149
|
+
});
|
|
162
150
|
|
|
163
151
|
expect(inputConfig).toEqual({
|
|
164
152
|
serializer: {
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"bundler-plugin-defaults.d.ts","sourceRoot":"","sources":["../src/bundler-plugin-defaults.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEtD,wBAAgB,wBAAwB,IAAI,QAAQ,CAAC,cAAc,CAAC,CAOnE"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"bundler-plugin-defaults.js","sourceRoot":"","sources":["../src/bundler-plugin-defaults.ts"],"names":[],"mappings":";;;AAEA,SAAgB,wBAAwB;IACtC,OAAO;QACL,wBAAwB,EAAE,IAAI;QAC9B,2BAA2B,EAAE,IAAI;QACjC,oBAAoB,EAAE,IAAI;QAC1B,SAAS,EAAE,KAAK;KACjB,CAAC;AACJ,CAAC;AAPD,4DAOC"}
|