@squiz/dxp-cli-next 5.23.1 → 5.24.0-develop.2

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.
@@ -36,4 +36,12 @@ describe('dxp', () => {
36
36
  'porter Porter Service Commands',
37
37
  ]);
38
38
  });
39
+ it('should show "page" command if ENABLE_PAGE_LAYOUTS="true"', () => {
40
+ var _a;
41
+ process.env.ENABLE_PAGE_LAYOUTS = 'true';
42
+ const { stdout } = (0, helpers_1.runCLI)(process.cwd(), ['--help']);
43
+ const commandsText = (_a = stdout
44
+ .match(/Commands\:(.*)/is)) === null || _a === void 0 ? void 0 : _a[1].split('\n').map(a => a.trim()).filter(a => !!a);
45
+ expect(commandsText).toContain('page Page Content Service Commands');
46
+ });
39
47
  });
@@ -29,6 +29,7 @@ describe('datastoreBlueprintList', () => {
29
29
  },
30
30
  ]);
31
31
  const program = (0, list_1.default)();
32
+ jest.spyOn(program, 'error').mockImplementation();
32
33
  yield program.parseAsync([
33
34
  'node',
34
35
  'dxp-cli',
@@ -66,6 +66,7 @@ describe('bundleJobContexts', () => {
66
66
  region: 'au',
67
67
  });
68
68
  const program = (0, bundle_1.default)();
69
+ jest.spyOn(program, 'error').mockImplementation();
69
70
  yield program.parseAsync([
70
71
  'node',
71
72
  'dxp-cli',
package/lib/dxp.js CHANGED
@@ -17,6 +17,7 @@ const job_runner_1 = __importDefault(require("./job-runner"));
17
17
  const datastore_1 = __importDefault(require("./datastore"));
18
18
  const cdp_1 = __importDefault(require("./cdp"));
19
19
  const porter_1 = __importDefault(require("./porter"));
20
+ const page_1 = __importDefault(require("./page"));
20
21
  const program = new commander_1.default.Command();
21
22
  const packageJson = require('../package.json');
22
23
  const version = packageJson.version;
@@ -32,6 +33,9 @@ program
32
33
  if (process.env.ENABLE_PORTER === 'true') {
33
34
  program.addCommand(porter_1.default);
34
35
  }
36
+ if (process.env.ENABLE_PAGE_LAYOUTS === 'true') {
37
+ program.addCommand(page_1.default);
38
+ }
35
39
  program
36
40
  .action(() => {
37
41
  program.help();
@@ -21,6 +21,7 @@ describe('listJobs', () => {
21
21
  .get('/job/latest?&page%5Bsize%5D=5')
22
22
  .reply(200, [{ name: 'simple-job', type: 'job', version: '1.0.0' }]);
23
23
  const program = (0, listJobs_1.default)();
24
+ jest.spyOn(program, 'error').mockImplementation();
24
25
  yield program.parseAsync([
25
26
  'node',
26
27
  'dxp-cli',
@@ -18,6 +18,7 @@ describe('upload job', () => {
18
18
  it('correctly displays the console logs for uploading the job', () => __awaiter(void 0, void 0, void 0, function* () {
19
19
  process.env.ENABLE_OVERRIDE_JOB_RUNNER_URL = 'true';
20
20
  const program = (0, uploadJob_1.default)();
21
+ jest.spyOn(program, 'error').mockImplementation();
21
22
  yield program.parseAsync([
22
23
  'node',
23
24
  'dxp-cli',
@@ -40,6 +40,7 @@ describe('listJobContexts', () => {
40
40
  },
41
41
  ]);
42
42
  const program = (0, listJobContexts_1.default)();
43
+ jest.spyOn(program, 'error').mockImplementation();
43
44
  yield program.parseAsync([
44
45
  'node',
45
46
  'dxp-cli',
@@ -43,6 +43,7 @@ describe('listJobExecutions', () => {
43
43
  },
44
44
  ]);
45
45
  const program = (0, listJobExecutions_1.default)();
46
+ jest.spyOn(program, 'error').mockImplementation();
46
47
  yield program.parseAsync([
47
48
  'node',
48
49
  'dxp-cli',
@@ -32,6 +32,7 @@ describe('terminateJob', () => {
32
32
  version: '1.0.1',
33
33
  });
34
34
  const program = (0, terminateJob_1.default)();
35
+ jest.spyOn(program, 'error').mockImplementation();
35
36
  yield program.parseAsync([
36
37
  'node',
37
38
  'dxp-cli',
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ declare const pageCommand: Command;
3
+ export default pageCommand;
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const commander_1 = require("commander");
7
+ const layouts_1 = __importDefault(require("./layouts/layouts"));
8
+ const pageCommand = new commander_1.Command('page');
9
+ pageCommand
10
+ .description('Page Content Service Commands')
11
+ .addCommand((0, layouts_1.default)());
12
+ exports.default = pageCommand;
@@ -0,0 +1,5 @@
1
+ import { Command } from 'commander';
2
+ import { Logger } from '@squiz/dx-logger-lib';
3
+ export declare const logger: Logger;
4
+ declare const createDeployCommand: () => Command;
5
+ export default createDeployCommand;
@@ -0,0 +1,108 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.logger = void 0;
16
+ const commander_1 = require("commander");
17
+ const cli_color_1 = __importDefault(require("cli-color"));
18
+ const ApiService_1 = require("../../../ApiService");
19
+ const ApplicationConfig_1 = require("../../../ApplicationConfig");
20
+ const constants_1 = require("../../../constants");
21
+ const definitions_1 = require("../../utils/definitions");
22
+ const dx_logger_lib_1 = require("@squiz/dx-logger-lib");
23
+ exports.logger = (0, dx_logger_lib_1.getLogger)({
24
+ name: 'upload-layout',
25
+ format: 'human',
26
+ });
27
+ const createDeployCommand = () => {
28
+ const deployCommand = new commander_1.Command()
29
+ .name('deploy')
30
+ .addOption(new commander_1.Option('--config <string>', 'File path to the page layout config file').default('./page-layout.yaml'))
31
+ .addOption(new commander_1.Option('-cu, --content-service-url <string>', 'Override the content service url from login')
32
+ .env('CONTENT_SERVICE_URL')
33
+ .hideHelp(true))
34
+ .addOption(new commander_1.Option('--dxp-base-url <baseURL>', 'DXP Console URL').env('DXP_BASE_URL'))
35
+ .addOption(new commander_1.Option('-t, --tenant <string>', 'Tenant ID to deploy to. If not provided will use configured tenant from login').env('SQUIZ_DXP_TENANT_ID'))
36
+ .addOption(new commander_1.Option('--dry-run', 'Run all pre-deployment processes without deploying').default(false))
37
+ .action((options) => __awaiter(void 0, void 0, void 0, function* () {
38
+ var _a, _b, _c, _d;
39
+ if (options.contentServiceUrl) {
40
+ console.log(`NOTICE: CONTENT_SERVICE_URL is set and will deploy to ${options.contentServiceUrl}`);
41
+ }
42
+ const maybeConfig = yield maybeGetApplicationConfig();
43
+ const baseUrl = (_b = (_a = options.dxpBaseUrl) !== null && _a !== void 0 ? _a : maybeConfig === null || maybeConfig === void 0 ? void 0 : maybeConfig.baseUrl) !== null && _b !== void 0 ? _b : constants_1.PRODUCTION_URL;
44
+ const apiService = new ApiService_1.ApiService({
45
+ baseUrl,
46
+ tenantId: (_c = options.tenant) !== null && _c !== void 0 ? _c : maybeConfig === null || maybeConfig === void 0 ? void 0 : maybeConfig.tenant,
47
+ });
48
+ const contentServiceUrl = (_d = options.contentServiceUrl) !== null && _d !== void 0 ? _d : new URL('/__dxp/service/components-content', baseUrl).toString();
49
+ const layoutFile = options.config;
50
+ exports.logger.info(`Loading layout data from the file ${layoutFile}`);
51
+ try {
52
+ const layout = yield (0, definitions_1.loadLayoutDefinition)(layoutFile);
53
+ if (layout !== undefined) {
54
+ const response = yield uploadLayout(apiService.client, layout, contentServiceUrl, options.dryRun);
55
+ if (!options.dryRun) {
56
+ exports.logger.info(`Layout "${layout.name}" version ${response.data.version} deployed successfully.`);
57
+ }
58
+ else {
59
+ exports.logger.info(`Layout "${layout.name}" dry run successful.`);
60
+ }
61
+ }
62
+ }
63
+ catch (error) {
64
+ if (!!process.env.DEBUG && error.stack) {
65
+ deployCommand.error(error.stack);
66
+ }
67
+ if (error instanceof ApiService_1.InvalidLoginSessionError) {
68
+ deployCommand.error(cli_color_1.default.red('ERROR:', 'Login session expired. Please login again.'));
69
+ }
70
+ if (error instanceof ApiService_1.InvalidTenantError) {
71
+ deployCommand.error(cli_color_1.default.red('ERROR:', 'Cannot deploy to specified tenant'));
72
+ }
73
+ if (error.message) {
74
+ deployCommand.error(cli_color_1.default.red('ERROR:', error.message));
75
+ }
76
+ else {
77
+ deployCommand.error(cli_color_1.default.red('ERROR:', 'An unknown error occurred'));
78
+ }
79
+ }
80
+ }));
81
+ return deployCommand;
82
+ };
83
+ exports.default = createDeployCommand;
84
+ function uploadLayout(client, layout, contentServiceUrl, dryRun) {
85
+ return __awaiter(this, void 0, void 0, function* () {
86
+ try {
87
+ const queryParam = dryRun ? '?_dryRun=true' : '';
88
+ const response = yield client.post('/page-layout' + queryParam, layout, {
89
+ baseURL: contentServiceUrl,
90
+ });
91
+ if (response.status === 200) {
92
+ return response;
93
+ }
94
+ throw new Error(response.data.message);
95
+ }
96
+ catch (error) {
97
+ throw error;
98
+ }
99
+ });
100
+ }
101
+ function maybeGetApplicationConfig() {
102
+ return __awaiter(this, void 0, void 0, function* () {
103
+ try {
104
+ return yield (0, ApplicationConfig_1.fetchApplicationConfig)();
105
+ }
106
+ catch (_a) { }
107
+ });
108
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,162 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ jest.mock('../../utils/definitions');
16
+ jest.mock('@squiz/dx-logger-lib', () => ({
17
+ getLogger: () => {
18
+ return {
19
+ info: mockLoggerInfoFn,
20
+ };
21
+ },
22
+ }));
23
+ const mockLoggerInfoFn = jest.fn();
24
+ const nock_1 = __importDefault(require("nock"));
25
+ const deploy_1 = __importDefault(require("./deploy"));
26
+ const definitions_1 = require("../../utils/definitions");
27
+ function createMockArgs(opts) {
28
+ const args = ['node', 'dxp-cli', 'deploy'];
29
+ if (opts.config)
30
+ args.push('--config', opts.config);
31
+ if (opts.contentServiceUrl)
32
+ args.push('--content-service-url', opts.contentServiceUrl);
33
+ if (opts.tenant)
34
+ args.push('--tenant', opts.tenant);
35
+ if (opts.dxpBaseUrl)
36
+ args.push('--dxp-base-url', opts.dxpBaseUrl);
37
+ if (opts.dryRun)
38
+ args.push('--dry-run');
39
+ return args;
40
+ }
41
+ describe('deployCommand', () => {
42
+ let logSpy;
43
+ let errorSpy;
44
+ beforeAll(() => {
45
+ process.env.SQUIZ_DXP_TENANT_ID = 'myTenant';
46
+ });
47
+ beforeEach(() => {
48
+ logSpy = jest.spyOn(console, 'log').mockImplementation(() => { });
49
+ if (!nock_1.default.isActive()) {
50
+ nock_1.default.activate();
51
+ }
52
+ nock_1.default.cleanAll();
53
+ });
54
+ afterEach(() => {
55
+ jest.clearAllMocks();
56
+ });
57
+ it('correctly handles command option defaults', () => __awaiter(void 0, void 0, void 0, function* () {
58
+ const program = (0, deploy_1.default)();
59
+ yield program.parseAsync(createMockArgs({}));
60
+ const opts = program.opts();
61
+ expect(opts.config).toEqual('./page-layout.yaml');
62
+ expect(opts.tenant).toEqual('myTenant');
63
+ expect(opts.dryRun).toEqual(false);
64
+ }));
65
+ it('correctly handles command arguments', () => __awaiter(void 0, void 0, void 0, function* () {
66
+ const config = './src/__tests__/layout.yaml';
67
+ const contentServiceUrl = 'http://localhost:9999';
68
+ const tenant = 'myTenant';
69
+ const dxpBaseUrl = 'http://dxp-base-url.com';
70
+ const dryRun = true;
71
+ const program = (0, deploy_1.default)();
72
+ yield program.parseAsync(createMockArgs({
73
+ config,
74
+ contentServiceUrl,
75
+ tenant,
76
+ dxpBaseUrl,
77
+ dryRun,
78
+ }));
79
+ const opts = program.opts();
80
+ expect(opts.config).toEqual(config);
81
+ expect(opts.contentServiceUrl).toEqual(contentServiceUrl);
82
+ expect(opts.tenant).toEqual(tenant);
83
+ expect(opts.dxpBaseUrl).toEqual(dxpBaseUrl);
84
+ expect(opts.dryRun).toEqual(true);
85
+ }));
86
+ it('logs a notice when contentServiceUrl is set', () => __awaiter(void 0, void 0, void 0, function* () {
87
+ const contentServiceUrl = 'http://localhost:9999';
88
+ const program = (0, deploy_1.default)();
89
+ yield program.parseAsync(createMockArgs({ contentServiceUrl }));
90
+ expect(logSpy).toHaveBeenCalledWith(`NOTICE: CONTENT_SERVICE_URL is set and will deploy to ${contentServiceUrl}`);
91
+ }));
92
+ it('deploys a layout successfully', () => __awaiter(void 0, void 0, void 0, function* () {
93
+ const file = './src/__tests__/layouts/page-layout.yaml';
94
+ const dxpBaseUrl = 'http://dxp-base-url.com';
95
+ const mockLayout = { name: 'Test Layout' };
96
+ const mockResponse = { name: 'Test Layout', version: '12345' };
97
+ definitions_1.loadLayoutDefinition.mockResolvedValue(mockLayout);
98
+ (0, nock_1.default)(dxpBaseUrl + '/__dxp/service/components-content')
99
+ .post('/page-layout', mockLayout)
100
+ .reply(200, mockResponse);
101
+ const program = (0, deploy_1.default)();
102
+ yield program.parseAsync(createMockArgs({ config: file, dxpBaseUrl }));
103
+ expect(mockLoggerInfoFn).toHaveBeenNthCalledWith(1, `Loading layout data from the file ${file}`);
104
+ expect(mockLoggerInfoFn).toHaveBeenNthCalledWith(2, 'Layout "Test Layout" version 12345 deployed successfully.');
105
+ }));
106
+ it('deploys a layout with dry-run option', () => __awaiter(void 0, void 0, void 0, function* () {
107
+ const file = './src/__tests__/layout.yaml';
108
+ const dxpBaseUrl = 'http://dxp-base-url.com';
109
+ const dryRun = true;
110
+ const mockLayout = { name: 'Test Layout' };
111
+ const mockResponse = { name: 'Test Layout', version: '12345' };
112
+ definitions_1.loadLayoutDefinition.mockResolvedValue(mockLayout);
113
+ (0, nock_1.default)(dxpBaseUrl + '/__dxp/service/components-content')
114
+ .post('/page-layout', mockLayout)
115
+ .query({ _dryRun: 'true' })
116
+ .reply(200, mockResponse);
117
+ const program = (0, deploy_1.default)();
118
+ yield program.parseAsync(createMockArgs({ config: file, dxpBaseUrl, dryRun }));
119
+ expect(mockLoggerInfoFn).toHaveBeenNthCalledWith(1, `Loading layout data from the file ${file}`);
120
+ expect(mockLoggerInfoFn).toHaveBeenNthCalledWith(2, 'Layout "Test Layout" dry run successful.');
121
+ }));
122
+ it('handles InvalidLoginSessionError', () => __awaiter(void 0, void 0, void 0, function* () {
123
+ const dxpBaseUrl = 'http://dxp-base-url.com';
124
+ (0, nock_1.default)(dxpBaseUrl + '/__dxp/service/components-content')
125
+ .post('/page-layout')
126
+ .reply(401, { message: 'Invalid request: no session' });
127
+ const program = (0, deploy_1.default)();
128
+ errorSpy = jest.spyOn(program, 'error').mockImplementation();
129
+ yield program.parseAsync(createMockArgs({ dxpBaseUrl }));
130
+ expect(errorSpy).toHaveBeenCalledWith(expect.stringContaining('Login session expired. Please login again.'));
131
+ }));
132
+ it('handles InvalidTenantError', () => __awaiter(void 0, void 0, void 0, function* () {
133
+ const dxpBaseUrl = 'http://dxp-base-url.com';
134
+ (0, nock_1.default)(dxpBaseUrl + '/__dxp/service/components-content')
135
+ .post('/page-layout')
136
+ .reply(400, { message: 'Cannot deploy to specified tenant' });
137
+ const program = (0, deploy_1.default)();
138
+ errorSpy = jest.spyOn(program, 'error').mockImplementation();
139
+ yield program.parseAsync(createMockArgs({ dxpBaseUrl }));
140
+ expect(errorSpy).toHaveBeenCalledWith(expect.stringContaining('Cannot deploy to specified tenant'));
141
+ }));
142
+ it('handles errors with message', () => __awaiter(void 0, void 0, void 0, function* () {
143
+ const dxpBaseUrl = 'http://dxp-base-url.com';
144
+ (0, nock_1.default)(dxpBaseUrl + '/__dxp/service/components-content')
145
+ .post('/page-layout')
146
+ .reply(400, { message: 'Invalid layout data!' });
147
+ const program = (0, deploy_1.default)();
148
+ errorSpy = jest.spyOn(program, 'error').mockImplementation();
149
+ yield program.parseAsync(createMockArgs({ dxpBaseUrl }));
150
+ expect(errorSpy).toHaveBeenCalledWith(expect.stringContaining('Invalid layout data!'));
151
+ }));
152
+ it('handles unknown errors', () => __awaiter(void 0, void 0, void 0, function* () {
153
+ const contentServiceUrl = 'http://localhost:9999';
154
+ (0, nock_1.default)(contentServiceUrl)
155
+ .post('/page-layout')
156
+ .reply(500, { data: 'Internal server error' });
157
+ const program = (0, deploy_1.default)();
158
+ errorSpy = jest.spyOn(program, 'error').mockImplementation();
159
+ yield program.parseAsync(createMockArgs({ contentServiceUrl }));
160
+ expect(errorSpy).toHaveBeenCalledWith(expect.stringContaining('An unknown error occurred'));
161
+ }));
162
+ });
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ declare const createLayoutsCommand: () => Command;
3
+ export default createLayoutsCommand;
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const commander_1 = require("commander");
7
+ const deploy_1 = __importDefault(require("./deploy/deploy"));
8
+ const createLayoutsCommand = () => {
9
+ const layoutsCommand = new commander_1.Command('layouts');
10
+ layoutsCommand
11
+ .description('Page Contents Layouts Commands')
12
+ .addCommand((0, deploy_1.default)());
13
+ return layoutsCommand;
14
+ };
15
+ exports.default = createLayoutsCommand;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const commander_1 = require("commander");
7
+ const layouts_1 = __importDefault(require("./layouts"));
8
+ const deploy_1 = __importDefault(require("./deploy/deploy"));
9
+ jest.mock('./deploy/deploy');
10
+ deploy_1.default.mockImplementation(() => {
11
+ return new commander_1.Command('mock-deploy');
12
+ });
13
+ describe('createLayoutsCommand', () => {
14
+ it('should create a layouts command with deploy subcommand', () => {
15
+ const layoutsCommand = (0, layouts_1.default)();
16
+ expect(layoutsCommand).toBeInstanceOf(commander_1.Command);
17
+ expect(layoutsCommand.name()).toBe('layouts');
18
+ expect(layoutsCommand.description()).toBe('Page Contents Layouts Commands');
19
+ expect(deploy_1.default).toHaveBeenCalled();
20
+ expect(layoutsCommand.commands.map(cmd => cmd.name())).toContain('mock-deploy');
21
+ });
22
+ });
@@ -0,0 +1,264 @@
1
+ import { z } from 'zod';
2
+ export declare function loadLayoutDefinition(layoutFile: string): Promise<LayoutDefinition>;
3
+ export declare const BaseLayoutDefinition: z.ZodObject<{
4
+ /**
5
+ * User defined identifier for a Page Layout
6
+ */
7
+ name: z.ZodString;
8
+ /**
9
+ * Name displayed in user interfaces
10
+ */
11
+ displayName: z.ZodString;
12
+ /**
13
+ * Description to provide extra information on the layout
14
+ */
15
+ description: z.ZodString;
16
+ /**
17
+ * Zone input structure configuration
18
+ *
19
+ * Declares the content structure that will be the input to the
20
+ * Handlebars template
21
+ */
22
+ zones: z.ZodRecord<z.ZodString, z.ZodObject<{
23
+ displayName: z.ZodString;
24
+ description: z.ZodString;
25
+ minNodes: z.ZodNumber;
26
+ maxNodes: z.ZodOptional<z.ZodNumber>;
27
+ }, "strip", z.ZodTypeAny, {
28
+ description: string;
29
+ displayName: string;
30
+ minNodes: number;
31
+ maxNodes?: number | undefined;
32
+ }, {
33
+ description: string;
34
+ displayName: string;
35
+ minNodes: number;
36
+ maxNodes?: number | undefined;
37
+ }>>;
38
+ /**
39
+ * Layout options
40
+ *
41
+ * Declares any configurable options that can be provided by a Content Editor
42
+ */
43
+ options: z.ZodRecord<z.ZodString, z.ZodObject<{
44
+ displayName: z.ZodString;
45
+ description: z.ZodString;
46
+ values: z.ZodArray<z.ZodString, "many">;
47
+ }, "strip", z.ZodTypeAny, {
48
+ values: string[];
49
+ description: string;
50
+ displayName: string;
51
+ }, {
52
+ values: string[];
53
+ description: string;
54
+ displayName: string;
55
+ }>>;
56
+ }, "strip", z.ZodTypeAny, {
57
+ name: string;
58
+ options: Record<string, {
59
+ values: string[];
60
+ description: string;
61
+ displayName: string;
62
+ }>;
63
+ description: string;
64
+ displayName: string;
65
+ zones: Record<string, {
66
+ description: string;
67
+ displayName: string;
68
+ minNodes: number;
69
+ maxNodes?: number | undefined;
70
+ }>;
71
+ }, {
72
+ name: string;
73
+ options: Record<string, {
74
+ values: string[];
75
+ description: string;
76
+ displayName: string;
77
+ }>;
78
+ description: string;
79
+ displayName: string;
80
+ zones: Record<string, {
81
+ description: string;
82
+ displayName: string;
83
+ minNodes: number;
84
+ maxNodes?: number | undefined;
85
+ }>;
86
+ }>;
87
+ export declare const InputLayoutDefinition: z.ZodObject<z.objectUtil.extendShape<{
88
+ /**
89
+ * User defined identifier for a Page Layout
90
+ */
91
+ name: z.ZodString;
92
+ /**
93
+ * Name displayed in user interfaces
94
+ */
95
+ displayName: z.ZodString;
96
+ /**
97
+ * Description to provide extra information on the layout
98
+ */
99
+ description: z.ZodString;
100
+ /**
101
+ * Zone input structure configuration
102
+ *
103
+ * Declares the content structure that will be the input to the
104
+ * Handlebars template
105
+ */
106
+ zones: z.ZodRecord<z.ZodString, z.ZodObject<{
107
+ displayName: z.ZodString;
108
+ description: z.ZodString;
109
+ minNodes: z.ZodNumber;
110
+ maxNodes: z.ZodOptional<z.ZodNumber>;
111
+ }, "strip", z.ZodTypeAny, {
112
+ description: string;
113
+ displayName: string;
114
+ minNodes: number;
115
+ maxNodes?: number | undefined;
116
+ }, {
117
+ description: string;
118
+ displayName: string;
119
+ minNodes: number;
120
+ maxNodes?: number | undefined;
121
+ }>>;
122
+ /**
123
+ * Layout options
124
+ *
125
+ * Declares any configurable options that can be provided by a Content Editor
126
+ */
127
+ options: z.ZodRecord<z.ZodString, z.ZodObject<{
128
+ displayName: z.ZodString;
129
+ description: z.ZodString;
130
+ values: z.ZodArray<z.ZodString, "many">;
131
+ }, "strip", z.ZodTypeAny, {
132
+ values: string[];
133
+ description: string;
134
+ displayName: string;
135
+ }, {
136
+ values: string[];
137
+ description: string;
138
+ displayName: string;
139
+ }>>;
140
+ }, {
141
+ entry: z.ZodString;
142
+ }>, "strip", z.ZodTypeAny, {
143
+ name: string;
144
+ options: Record<string, {
145
+ values: string[];
146
+ description: string;
147
+ displayName: string;
148
+ }>;
149
+ entry: string;
150
+ description: string;
151
+ displayName: string;
152
+ zones: Record<string, {
153
+ description: string;
154
+ displayName: string;
155
+ minNodes: number;
156
+ maxNodes?: number | undefined;
157
+ }>;
158
+ }, {
159
+ name: string;
160
+ options: Record<string, {
161
+ values: string[];
162
+ description: string;
163
+ displayName: string;
164
+ }>;
165
+ entry: string;
166
+ description: string;
167
+ displayName: string;
168
+ zones: Record<string, {
169
+ description: string;
170
+ displayName: string;
171
+ minNodes: number;
172
+ maxNodes?: number | undefined;
173
+ }>;
174
+ }>;
175
+ export declare const LayoutDefinition: z.ZodObject<z.objectUtil.extendShape<{
176
+ /**
177
+ * User defined identifier for a Page Layout
178
+ */
179
+ name: z.ZodString;
180
+ /**
181
+ * Name displayed in user interfaces
182
+ */
183
+ displayName: z.ZodString;
184
+ /**
185
+ * Description to provide extra information on the layout
186
+ */
187
+ description: z.ZodString;
188
+ /**
189
+ * Zone input structure configuration
190
+ *
191
+ * Declares the content structure that will be the input to the
192
+ * Handlebars template
193
+ */
194
+ zones: z.ZodRecord<z.ZodString, z.ZodObject<{
195
+ displayName: z.ZodString;
196
+ description: z.ZodString;
197
+ minNodes: z.ZodNumber;
198
+ maxNodes: z.ZodOptional<z.ZodNumber>;
199
+ }, "strip", z.ZodTypeAny, {
200
+ description: string;
201
+ displayName: string;
202
+ minNodes: number;
203
+ maxNodes?: number | undefined;
204
+ }, {
205
+ description: string;
206
+ displayName: string;
207
+ minNodes: number;
208
+ maxNodes?: number | undefined;
209
+ }>>;
210
+ /**
211
+ * Layout options
212
+ *
213
+ * Declares any configurable options that can be provided by a Content Editor
214
+ */
215
+ options: z.ZodRecord<z.ZodString, z.ZodObject<{
216
+ displayName: z.ZodString;
217
+ description: z.ZodString;
218
+ values: z.ZodArray<z.ZodString, "many">;
219
+ }, "strip", z.ZodTypeAny, {
220
+ values: string[];
221
+ description: string;
222
+ displayName: string;
223
+ }, {
224
+ values: string[];
225
+ description: string;
226
+ displayName: string;
227
+ }>>;
228
+ }, {
229
+ template: z.ZodString;
230
+ }>, "strip", z.ZodTypeAny, {
231
+ name: string;
232
+ options: Record<string, {
233
+ values: string[];
234
+ description: string;
235
+ displayName: string;
236
+ }>;
237
+ description: string;
238
+ displayName: string;
239
+ zones: Record<string, {
240
+ description: string;
241
+ displayName: string;
242
+ minNodes: number;
243
+ maxNodes?: number | undefined;
244
+ }>;
245
+ template: string;
246
+ }, {
247
+ name: string;
248
+ options: Record<string, {
249
+ values: string[];
250
+ description: string;
251
+ displayName: string;
252
+ }>;
253
+ description: string;
254
+ displayName: string;
255
+ zones: Record<string, {
256
+ description: string;
257
+ displayName: string;
258
+ minNodes: number;
259
+ maxNodes?: number | undefined;
260
+ }>;
261
+ template: string;
262
+ }>;
263
+ export declare type InputLayoutDefinition = z.infer<typeof InputLayoutDefinition>;
264
+ export declare type LayoutDefinition = z.infer<typeof LayoutDefinition>;
@@ -0,0 +1,138 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
26
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
27
+ return new (P || (P = Promise))(function (resolve, reject) {
28
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
29
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
30
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
31
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
32
+ });
33
+ };
34
+ var __rest = (this && this.__rest) || function (s, e) {
35
+ var t = {};
36
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
37
+ t[p] = s[p];
38
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
39
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
40
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
41
+ t[p[i]] = s[p[i]];
42
+ }
43
+ return t;
44
+ };
45
+ Object.defineProperty(exports, "__esModule", { value: true });
46
+ exports.LayoutDefinition = exports.InputLayoutDefinition = exports.BaseLayoutDefinition = exports.loadLayoutDefinition = void 0;
47
+ const fs = __importStar(require("node:fs/promises"));
48
+ const path = __importStar(require("node:path"));
49
+ const zod_1 = require("zod");
50
+ const yaml_1 = require("yaml");
51
+ function loadLayoutDefinition(layoutFile) {
52
+ return __awaiter(this, void 0, void 0, function* () {
53
+ try {
54
+ const _a = yield loadLayoutFromFile(layoutFile), { entry } = _a, layout = __rest(_a, ["entry"]);
55
+ const template = yield loadTemplate(path.dirname(layoutFile), entry);
56
+ const layoutDefinition = Object.assign(Object.assign({}, layout), { template });
57
+ exports.LayoutDefinition.parse(layoutDefinition);
58
+ return layoutDefinition;
59
+ }
60
+ catch (e) {
61
+ throw Error(`Failed loading layout definition: ${e.message}`);
62
+ }
63
+ });
64
+ }
65
+ exports.loadLayoutDefinition = loadLayoutDefinition;
66
+ function loadLayoutFromFile(layoutFile) {
67
+ return __awaiter(this, void 0, void 0, function* () {
68
+ const fileType = layoutFile.split('.').pop();
69
+ if (!fileType || !['yaml', 'json'].includes(fileType)) {
70
+ throw Error('Layout file must have a valid extension: [yaml|json]');
71
+ }
72
+ const content = yield fs.readFile(layoutFile, {
73
+ encoding: 'utf-8',
74
+ });
75
+ try {
76
+ const layout = (0, yaml_1.parse)(content);
77
+ exports.InputLayoutDefinition.parse(layout);
78
+ return layout;
79
+ }
80
+ catch (e) {
81
+ throw Error(`Failed to parse ${layoutFile}: ${e.message}`);
82
+ }
83
+ });
84
+ }
85
+ function loadTemplate(layoutDirectory, templateFile) {
86
+ return __awaiter(this, void 0, void 0, function* () {
87
+ try {
88
+ return yield fs.readFile(path.resolve(layoutDirectory, templateFile), {
89
+ encoding: 'utf-8',
90
+ });
91
+ }
92
+ catch (e) {
93
+ throw Error(`Failed loading template file "${templateFile}": ${e.message}`);
94
+ }
95
+ });
96
+ }
97
+ exports.BaseLayoutDefinition = zod_1.z.object({
98
+ /**
99
+ * User defined identifier for a Page Layout
100
+ */
101
+ name: zod_1.z.string().regex(/^[a-z][a-z0-9-_]*[a-z0-9]$/),
102
+ /**
103
+ * Name displayed in user interfaces
104
+ */
105
+ displayName: zod_1.z.string(),
106
+ /**
107
+ * Description to provide extra information on the layout
108
+ */
109
+ description: zod_1.z.string(),
110
+ /**
111
+ * Zone input structure configuration
112
+ *
113
+ * Declares the content structure that will be the input to the
114
+ * Handlebars template
115
+ */
116
+ zones: zod_1.z.record(zod_1.z.string(), zod_1.z.object({
117
+ displayName: zod_1.z.string(),
118
+ description: zod_1.z.string(),
119
+ minNodes: zod_1.z.number().min(0),
120
+ maxNodes: zod_1.z.number().min(1).optional(),
121
+ })),
122
+ /**
123
+ * Layout options
124
+ *
125
+ * Declares any configurable options that can be provided by a Content Editor
126
+ */
127
+ options: zod_1.z.record(zod_1.z.string(), zod_1.z.object({
128
+ displayName: zod_1.z.string(),
129
+ description: zod_1.z.string(),
130
+ values: zod_1.z.array(zod_1.z.string()),
131
+ })),
132
+ });
133
+ exports.InputLayoutDefinition = exports.BaseLayoutDefinition.extend({
134
+ entry: zod_1.z.string(),
135
+ });
136
+ exports.LayoutDefinition = exports.BaseLayoutDefinition.extend({
137
+ template: zod_1.z.string(),
138
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,290 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
26
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
27
+ return new (P || (P = Promise))(function (resolve, reject) {
28
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
29
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
30
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
31
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
32
+ });
33
+ };
34
+ Object.defineProperty(exports, "__esModule", { value: true });
35
+ const fs = __importStar(require("node:fs/promises"));
36
+ const definitions_1 = require("./definitions");
37
+ jest.mock('node:fs/promises');
38
+ describe('loadLayoutDefinition', () => {
39
+ const paintLayoutFileYaml = './some-dir/page-layout.yaml';
40
+ const paintLayoutFileJson = './some-dir/page-layout.json';
41
+ const yamlContent = `
42
+ name: test-layout
43
+ displayName: Test Layout
44
+ description: A test layout
45
+ zones:
46
+ main:
47
+ displayName: Main Zone
48
+ description: Main content area
49
+ minNodes: 1
50
+ options:
51
+ color:
52
+ displayName: Color
53
+ description: Color options
54
+ values: ['red', 'blue']
55
+ entry: template.hbs
56
+ `;
57
+ // missing "name" field
58
+ const yamlContentInvalidLayout = `
59
+ displayName: Test Layout
60
+ description: A test layout
61
+ zones:
62
+ main:
63
+ displayName: Main Zone
64
+ description: Main content area
65
+ minNodes: 1
66
+ options:
67
+ color:
68
+ displayName: Color
69
+ description: Color options
70
+ values: ['red', 'blue']
71
+ entry: template.hbs
72
+ `;
73
+ const jsonContent = JSON.stringify({
74
+ name: 'test-layout',
75
+ displayName: 'Test Layout',
76
+ description: 'A test layout',
77
+ zones: {
78
+ main: {
79
+ displayName: 'Main Zone',
80
+ description: 'Main content area',
81
+ minNodes: 1,
82
+ },
83
+ },
84
+ options: {
85
+ color: {
86
+ displayName: 'Color',
87
+ description: 'Color options',
88
+ values: ['red', 'blue'],
89
+ },
90
+ },
91
+ entry: 'template.hbs',
92
+ });
93
+ // missing "name" field
94
+ const jsonContentInvalidLayout = JSON.stringify({
95
+ displayName: 'Test Layout',
96
+ description: 'A test layout',
97
+ zones: {
98
+ main: {
99
+ displayName: 'Main Zone',
100
+ description: 'Main content area',
101
+ minNodes: 1,
102
+ },
103
+ },
104
+ options: {
105
+ color: {
106
+ displayName: 'Color',
107
+ description: 'Color options',
108
+ values: ['red', 'blue'],
109
+ },
110
+ },
111
+ entry: 'template.hbs',
112
+ });
113
+ const templateContent = '<div>{{content}}</div>';
114
+ beforeEach(() => {
115
+ jest.resetAllMocks();
116
+ });
117
+ it('should load layout definition from YAML file', () => __awaiter(void 0, void 0, void 0, function* () {
118
+ fs.readFile.mockImplementation((filePath) => {
119
+ if (filePath.endsWith('page-layout.yaml')) {
120
+ return yamlContent;
121
+ }
122
+ if (filePath.endsWith('template.hbs')) {
123
+ return templateContent;
124
+ }
125
+ throw new Error('File not found');
126
+ });
127
+ const layoutDefinition = yield (0, definitions_1.loadLayoutDefinition)(paintLayoutFileYaml);
128
+ expect(layoutDefinition).toEqual({
129
+ name: 'test-layout',
130
+ displayName: 'Test Layout',
131
+ description: 'A test layout',
132
+ zones: {
133
+ main: {
134
+ displayName: 'Main Zone',
135
+ description: 'Main content area',
136
+ minNodes: 1,
137
+ },
138
+ },
139
+ options: {
140
+ color: {
141
+ displayName: 'Color',
142
+ description: 'Color options',
143
+ values: ['red', 'blue'],
144
+ },
145
+ },
146
+ template: templateContent,
147
+ });
148
+ }));
149
+ it('should load layout definition from JSON file', () => __awaiter(void 0, void 0, void 0, function* () {
150
+ fs.readFile.mockImplementation((filePath) => {
151
+ if (filePath.endsWith('page-layout.yaml')) {
152
+ throw { code: 'ENOENT' };
153
+ }
154
+ if (filePath.endsWith('page-layout.json')) {
155
+ return jsonContent;
156
+ }
157
+ if (filePath.endsWith('template.hbs')) {
158
+ return templateContent;
159
+ }
160
+ throw new Error('File not found');
161
+ });
162
+ const layoutDefinition = yield (0, definitions_1.loadLayoutDefinition)(paintLayoutFileJson);
163
+ expect(layoutDefinition).toEqual({
164
+ name: 'test-layout',
165
+ displayName: 'Test Layout',
166
+ description: 'A test layout',
167
+ zones: {
168
+ main: {
169
+ displayName: 'Main Zone',
170
+ description: 'Main content area',
171
+ minNodes: 1,
172
+ },
173
+ },
174
+ options: {
175
+ color: {
176
+ displayName: 'Color',
177
+ description: 'Color options',
178
+ values: ['red', 'blue'],
179
+ },
180
+ },
181
+ template: templateContent,
182
+ });
183
+ }));
184
+ it('should throw validation error if invalid data for YAML', () => __awaiter(void 0, void 0, void 0, function* () {
185
+ fs.readFile.mockImplementation((filePath) => {
186
+ if (filePath.endsWith('page-layout.yaml')) {
187
+ return yamlContentInvalidLayout;
188
+ }
189
+ if (filePath.endsWith('template.hbs')) {
190
+ return templateContent;
191
+ }
192
+ throw new Error('File not found');
193
+ });
194
+ yield expect((0, definitions_1.loadLayoutDefinition)(paintLayoutFileYaml)).rejects
195
+ .toThrowErrorMatchingInlineSnapshot(`
196
+ "Failed loading layout definition: Failed to parse ./some-dir/page-layout.yaml: [
197
+ {
198
+ "code": "invalid_type",
199
+ "expected": "string",
200
+ "received": "undefined",
201
+ "path": [
202
+ "name"
203
+ ],
204
+ "message": "Required"
205
+ }
206
+ ]"
207
+ `);
208
+ }));
209
+ it('should throw validation error if invalid data for JSON', () => __awaiter(void 0, void 0, void 0, function* () {
210
+ fs.readFile.mockImplementation((filePath) => {
211
+ if (filePath.endsWith('page-layout.json')) {
212
+ return jsonContentInvalidLayout;
213
+ }
214
+ if (filePath.endsWith('template.hbs')) {
215
+ return templateContent;
216
+ }
217
+ throw new Error('File not found');
218
+ });
219
+ yield expect((0, definitions_1.loadLayoutDefinition)(paintLayoutFileJson)).rejects
220
+ .toThrowErrorMatchingInlineSnapshot(`
221
+ "Failed loading layout definition: Failed to parse ./some-dir/page-layout.json: [
222
+ {
223
+ "code": "invalid_type",
224
+ "expected": "string",
225
+ "received": "undefined",
226
+ "path": [
227
+ "name"
228
+ ],
229
+ "message": "Required"
230
+ }
231
+ ]"
232
+ `);
233
+ }));
234
+ it('should throw an error if YAML layout file has invalid YAML', () => __awaiter(void 0, void 0, void 0, function* () {
235
+ fs.readFile.mockImplementation((filePath) => {
236
+ if (filePath.endsWith('page-layout.yaml')) {
237
+ return 'name: invalid yaml, entry: template.hbs';
238
+ }
239
+ if (filePath.endsWith('page-layout.json')) {
240
+ return jsonContent;
241
+ }
242
+ if (filePath.endsWith('template.hbs')) {
243
+ return templateContent;
244
+ }
245
+ throw new Error('File not found');
246
+ });
247
+ yield expect((0, definitions_1.loadLayoutDefinition)(paintLayoutFileYaml)).rejects
248
+ .toThrowErrorMatchingInlineSnapshot(`
249
+ "Failed loading layout definition: Failed to parse ./some-dir/page-layout.yaml: Nested mappings are not allowed in compact mappings at line 1, column 7:
250
+
251
+ name: invalid yaml, entry: template.hbs
252
+ ^
253
+ "
254
+ `);
255
+ }));
256
+ it('should throw an error if JSON layout file has invalid JSON', () => __awaiter(void 0, void 0, void 0, function* () {
257
+ fs.readFile.mockImplementation((filePath) => {
258
+ if (filePath.endsWith('page-layout.yaml')) {
259
+ throw { code: 'ENOENT' };
260
+ }
261
+ if (filePath.endsWith('page-layout.json')) {
262
+ return '{invalid: json';
263
+ }
264
+ if (filePath.endsWith('template.hbs')) {
265
+ return templateContent;
266
+ }
267
+ throw new Error('File not found');
268
+ });
269
+ yield expect((0, definitions_1.loadLayoutDefinition)(paintLayoutFileJson)).rejects
270
+ .toThrowErrorMatchingInlineSnapshot(`
271
+ "Failed loading layout definition: Failed to parse ./some-dir/page-layout.json: Flow map must end with a } at line 1, column 15:
272
+
273
+ {invalid: json
274
+ ^
275
+ "
276
+ `);
277
+ }));
278
+ it('should throw an error if layout file do not have valid extension', () => __awaiter(void 0, void 0, void 0, function* () {
279
+ yield expect((0, definitions_1.loadLayoutDefinition)('/foo/paint-layout.html')).rejects.toThrow('Layout file must have a valid extension: [yaml|json]');
280
+ }));
281
+ it('should throw an error if template file is not found', () => __awaiter(void 0, void 0, void 0, function* () {
282
+ fs.readFile.mockImplementation((filePath) => {
283
+ if (filePath.endsWith('page-layout.yaml')) {
284
+ return yamlContent;
285
+ }
286
+ throw Error('File not found');
287
+ });
288
+ yield expect((0, definitions_1.loadLayoutDefinition)(paintLayoutFileYaml)).rejects.toThrow('Failed loading layout definition: Failed loading template file "template.hbs": File not found');
289
+ }));
290
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@squiz/dxp-cli-next",
3
- "version": "5.23.1",
3
+ "version": "5.24.0-develop.2",
4
4
  "repository": {
5
5
  "url": "https://gitlab.squiz.net/dxp/dxp-cli-next"
6
6
  },
@@ -41,9 +41,10 @@
41
41
  ],
42
42
  "dependencies": {
43
43
  "@apidevtools/swagger-parser": "10.1.0",
44
- "@squiz/component-cli-lib": "1.72.6",
44
+ "@squiz/component-cli-lib": "1.72.8",
45
45
  "@squiz/dxp-porter-shared": "0.4.0",
46
46
  "@squiz/local-component-dev-ui": "0.6.18",
47
+ "@squiz/dx-logger-lib": "^1.65.1",
47
48
  "axios": "1.1.3",
48
49
  "cli-color": "2.0.3",
49
50
  "commander": "9.4.0",