@sitecore-content-sdk/nextjs 0.2.0-beta.4 → 0.2.0-beta.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/dist/cjs/components/Link.js +0 -2
- package/dist/cjs/components/NextImage.js +6 -25
- package/dist/cjs/components/RichText.js +0 -5
- package/dist/cjs/editing/editing-render-middleware.js +1 -1
- package/dist/cjs/middleware/redirects-middleware.js +2 -2
- package/dist/cjs/tools/codegen/extract-components.js +64 -0
- package/dist/cjs/tools/codegen/utils.js +255 -0
- package/dist/cjs/tools/index.js +3 -1
- package/dist/esm/components/Link.js +1 -3
- package/dist/esm/components/NextImage.js +6 -25
- package/dist/esm/components/RichText.js +1 -3
- package/dist/esm/editing/editing-render-middleware.js +1 -1
- package/dist/esm/middleware/redirects-middleware.js +2 -2
- package/dist/esm/tools/codegen/extract-components.js +57 -0
- package/dist/esm/tools/codegen/utils.js +212 -0
- package/dist/esm/tools/index.js +1 -0
- package/package.json +20 -16
- package/types/components/ComponentPropsContext.d.ts +1 -1
- package/types/components/RichText.d.ts +1 -14
- package/types/tools/codegen/extract-components.d.ts +10 -0
- package/types/tools/codegen/utils.d.ts +40 -0
- package/types/tools/index.d.ts +1 -0
|
@@ -49,7 +49,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
49
49
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
50
50
|
exports.Link = void 0;
|
|
51
51
|
const react_1 = __importStar(require("react"));
|
|
52
|
-
const prop_types_1 = __importDefault(require("prop-types"));
|
|
53
52
|
const link_1 = __importDefault(require("next/link"));
|
|
54
53
|
const react_2 = require("@sitecore-content-sdk/react");
|
|
55
54
|
/**
|
|
@@ -90,4 +89,3 @@ exports.Link = (0, react_1.forwardRef)((props, ref) => {
|
|
|
90
89
|
return (react_1.default.createElement(react_2.Link, Object.assign({}, reactLinkProps, { ref: ref }, (process.env.TEST ? { 'data-react-link': true } : {}))));
|
|
91
90
|
});
|
|
92
91
|
exports.Link.displayName = 'NextLink';
|
|
93
|
-
exports.Link.propTypes = Object.assign({ internalLinkMatcher: prop_types_1.default.instanceOf(RegExp) }, react_2.LinkPropTypes);
|
|
@@ -16,15 +16,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
exports.NextImage = void 0;
|
|
18
18
|
const media_1 = require("@sitecore-content-sdk/core/media");
|
|
19
|
-
const prop_types_1 = __importDefault(require("prop-types"));
|
|
20
19
|
const react_1 = __importDefault(require("react"));
|
|
21
20
|
const react_2 = require("@sitecore-content-sdk/react");
|
|
22
21
|
const image_1 = __importDefault(require("next/image"));
|
|
23
|
-
const react_3 = require("@sitecore-content-sdk/react");
|
|
24
|
-
const react_4 = require("@sitecore-content-sdk/react");
|
|
25
22
|
const layout_1 = require("@sitecore-content-sdk/core/layout");
|
|
26
|
-
exports.NextImage = (0, react_2.withFieldMetadata)((0,
|
|
27
|
-
var _b;
|
|
23
|
+
exports.NextImage = (0, react_2.withFieldMetadata)((0, react_2.withEmptyFieldEditingComponent)((_a) => {
|
|
24
|
+
var _b, _c;
|
|
28
25
|
var { editable = true, imageParams, field, mediaUrlPrefix, fill, priority } = _a, otherProps = __rest(_a, ["editable", "imageParams", "field", "mediaUrlPrefix", "fill", "priority"]);
|
|
29
26
|
const sitecoreContext = react_1.default.useContext(react_2.SitecoreContextReactContext);
|
|
30
27
|
// next handles src and we use a custom loader,
|
|
@@ -43,9 +40,10 @@ exports.NextImage = (0, react_2.withFieldMetadata)((0, react_3.withEmptyFieldEdi
|
|
|
43
40
|
if (!img) {
|
|
44
41
|
return null;
|
|
45
42
|
}
|
|
46
|
-
// disable image optimization for Edit
|
|
43
|
+
// disable image optimization for Edit / Preview / Component rendering, but preserve original value if true
|
|
47
44
|
const unoptimized = otherProps.unoptimized ||
|
|
48
|
-
((_b = sitecoreContext.context) === null || _b === void 0 ? void 0 : _b.
|
|
45
|
+
((_b = sitecoreContext.context) === null || _b === void 0 ? void 0 : _b.renderingType) === layout_1.RenderingType.Component ||
|
|
46
|
+
((_c = sitecoreContext.context) === null || _c === void 0 ? void 0 : _c.pageState) !== layout_1.LayoutServicePageState.Normal;
|
|
49
47
|
const attrs = Object.assign(Object.assign(Object.assign({}, img), otherProps), { fill,
|
|
50
48
|
priority, src: media_1.mediaApi.updateImageUrl(img.src, imageParams, mediaUrlPrefix), unoptimized });
|
|
51
49
|
const imageProps = Object.assign(Object.assign({}, attrs), {
|
|
@@ -61,22 +59,5 @@ exports.NextImage = (0, react_2.withFieldMetadata)((0, react_3.withEmptyFieldEdi
|
|
|
61
59
|
return (react_1.default.createElement(image_1.default, Object.assign({ alt: "" }, imageProps, (process.env.TEST ? { 'data-unoptimized': unoptimized } : {}))));
|
|
62
60
|
}
|
|
63
61
|
return null; // we can't handle the truth
|
|
64
|
-
}, { defaultEmptyFieldEditingComponent:
|
|
65
|
-
exports.NextImage.propTypes = {
|
|
66
|
-
field: prop_types_1.default.oneOfType([
|
|
67
|
-
prop_types_1.default.shape({
|
|
68
|
-
src: prop_types_1.default.string.isRequired,
|
|
69
|
-
}),
|
|
70
|
-
prop_types_1.default.shape({
|
|
71
|
-
value: prop_types_1.default.object,
|
|
72
|
-
}),
|
|
73
|
-
]),
|
|
74
|
-
editable: prop_types_1.default.bool,
|
|
75
|
-
mediaUrlPrefix: prop_types_1.default.instanceOf(RegExp),
|
|
76
|
-
imageParams: prop_types_1.default.objectOf(prop_types_1.default.oneOfType([prop_types_1.default.number.isRequired, prop_types_1.default.string.isRequired]).isRequired),
|
|
77
|
-
emptyFieldEditingComponent: prop_types_1.default.oneOfType([
|
|
78
|
-
prop_types_1.default.object,
|
|
79
|
-
prop_types_1.default.func,
|
|
80
|
-
]),
|
|
81
|
-
};
|
|
62
|
+
}, { defaultEmptyFieldEditingComponent: react_2.DefaultEmptyFieldEditingComponentImage }));
|
|
82
63
|
exports.NextImage.displayName = 'NextImage';
|
|
@@ -43,13 +43,9 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
43
43
|
}
|
|
44
44
|
return t;
|
|
45
45
|
};
|
|
46
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
47
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
48
|
-
};
|
|
49
46
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
50
47
|
exports.RichText = exports.prefetched = void 0;
|
|
51
48
|
const react_1 = __importStar(require("react"));
|
|
52
|
-
const prop_types_1 = __importDefault(require("prop-types"));
|
|
53
49
|
const router_1 = require("next/router");
|
|
54
50
|
const react_2 = require("@sitecore-content-sdk/react");
|
|
55
51
|
exports.prefetched = {};
|
|
@@ -103,5 +99,4 @@ const RichText = (props) => {
|
|
|
103
99
|
return react_1.default.createElement(react_2.RichText, Object.assign({ ref: richTextRef, editable: editable }, rest));
|
|
104
100
|
};
|
|
105
101
|
exports.RichText = RichText;
|
|
106
|
-
exports.RichText.propTypes = Object.assign({ internalLinksSelector: prop_types_1.default.string }, react_2.RichTextPropTypes);
|
|
107
102
|
exports.RichText.displayName = 'NextRichText';
|
|
@@ -105,7 +105,7 @@ class EditingRenderMiddleware extends render_middleware_1.RenderMiddlewareBase {
|
|
|
105
105
|
language: query.sc_lang,
|
|
106
106
|
site: query.sc_site,
|
|
107
107
|
mode: 'library',
|
|
108
|
-
dataSourceId: query.
|
|
108
|
+
dataSourceId: query.dataSourceId,
|
|
109
109
|
version: query.sc_version,
|
|
110
110
|
}, {
|
|
111
111
|
maxAge: 3,
|
|
@@ -242,9 +242,9 @@ class RedirectsMiddleware extends middleware_1.MiddlewareBase {
|
|
|
242
242
|
return false;
|
|
243
243
|
})
|
|
244
244
|
.join('&');
|
|
245
|
-
const newUrl = new URL(`${url.pathname}?${newQueryString}`, url.origin);
|
|
245
|
+
const newUrl = new URL(`${url.pathname.toLowerCase()}?${newQueryString}`, url.origin);
|
|
246
246
|
url.search = newUrl.search;
|
|
247
|
-
url.pathname = newUrl.pathname;
|
|
247
|
+
url.pathname = newUrl.pathname.toLocaleLowerCase();
|
|
248
248
|
url.href = newUrl.href;
|
|
249
249
|
return url;
|
|
250
250
|
}
|
|
@@ -0,0 +1,64 @@
|
|
|
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.extractComponents = void 0;
|
|
16
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
17
|
+
const utils_1 = require("./utils");
|
|
18
|
+
const core_1 = require("@sitecore-content-sdk/core");
|
|
19
|
+
const tools_1 = require("@sitecore-content-sdk/core/tools");
|
|
20
|
+
/**
|
|
21
|
+
* Extracts components from the app folder and sends them to XMCloud.
|
|
22
|
+
* @param {ExtractComponentsConfig} args - Config for components extraction
|
|
23
|
+
*/
|
|
24
|
+
const extractComponents = (args) => {
|
|
25
|
+
const authParams = {
|
|
26
|
+
clientId: process.env.SITECORE_AUTH_CLIENT_ID || '',
|
|
27
|
+
clientSecret: process.env.SITECORE_AUTH_CLIENT_SECRET || '',
|
|
28
|
+
endpoint: process.env.SITECORE_AUTH_ENDPOINT || core_1.constants.DEFAULT_SITECORE_AUTH_ENDPOINT,
|
|
29
|
+
audience: process.env.SITECORE_AUTH_AUDIENCE || core_1.constants.DEFAULT_SITECORE_AUTH_AUDIENCE,
|
|
30
|
+
};
|
|
31
|
+
return () => __awaiter(void 0, void 0, void 0, function* () {
|
|
32
|
+
if (!(0, utils_1.validateDeployContext)()) {
|
|
33
|
+
console.log(chalk_1.default.yellow('Skipping code extraction, not in deploy context'));
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
if (!(0, utils_1.validateConsent)()) {
|
|
37
|
+
console.log(chalk_1.default.yellow('Skipping code extraction, consent not given'));
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
const basePath = process.cwd();
|
|
41
|
+
try {
|
|
42
|
+
const bearer = yield (0, tools_1.fetchBearerToken)(authParams);
|
|
43
|
+
if (!bearer) {
|
|
44
|
+
console.error(chalk_1.default.red('Failed to get bearer token, aborting code extraction'));
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
const componentPaths = yield (0, utils_1.resolveComponentImportFiles)(basePath, args.componentMapPath);
|
|
48
|
+
const codeDispatches = Array.from(componentPaths, (mapEntry) => (0, utils_1.sendCode)({
|
|
49
|
+
file: {
|
|
50
|
+
name: mapEntry[0],
|
|
51
|
+
path: mapEntry[1],
|
|
52
|
+
type: utils_1.ExtractedFileType.Component,
|
|
53
|
+
},
|
|
54
|
+
token: bearer,
|
|
55
|
+
edgeUrl: args.scConfig.api.edge.edgeUrl,
|
|
56
|
+
}));
|
|
57
|
+
yield Promise.all(codeDispatches);
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
console.error(chalk_1.default.red('Error during component extraction:', error));
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
};
|
|
64
|
+
exports.extractComponents = extractComponents;
|
|
@@ -0,0 +1,255 @@
|
|
|
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 () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
36
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
37
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
38
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
39
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
40
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
41
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
42
|
+
});
|
|
43
|
+
};
|
|
44
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
45
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
46
|
+
};
|
|
47
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
48
|
+
exports.sendCode = exports.resolveComponentImportFiles = exports.validateDeployContext = exports.validateConsent = exports.ExtractedFileType = void 0;
|
|
49
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
50
|
+
const path_1 = __importDefault(require("path"));
|
|
51
|
+
const fs_1 = __importDefault(require("fs"));
|
|
52
|
+
const ts = __importStar(require("typescript"));
|
|
53
|
+
const core_1 = require("@sitecore-content-sdk/core");
|
|
54
|
+
/**
|
|
55
|
+
* Type of file to be sent to the mesh endpoint
|
|
56
|
+
*/
|
|
57
|
+
var ExtractedFileType;
|
|
58
|
+
(function (ExtractedFileType) {
|
|
59
|
+
ExtractedFileType["Component"] = "component";
|
|
60
|
+
ExtractedFileType["Json"] = "json";
|
|
61
|
+
ExtractedFileType["Package"] = "package.json";
|
|
62
|
+
})(ExtractedFileType || (exports.ExtractedFileType = ExtractedFileType = {}));
|
|
63
|
+
/**
|
|
64
|
+
* Validates consent for code extraction procedures
|
|
65
|
+
* @returns {boolean} - true if consent is given, false otherwise
|
|
66
|
+
*/
|
|
67
|
+
const validateConsent = () => {
|
|
68
|
+
if (!process.env.EXTRACT_CONSENT) {
|
|
69
|
+
console.log(chalk_1.default.yellow('EXTRACT_CONSENT is not set'));
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
return true;
|
|
73
|
+
};
|
|
74
|
+
exports.validateConsent = validateConsent;
|
|
75
|
+
/**
|
|
76
|
+
* Validates if the current operation is done in Vercel, Netlify or XMCloud
|
|
77
|
+
* deploy context
|
|
78
|
+
* @returns {boolean} - true if in deploy context, false otherwise
|
|
79
|
+
*/
|
|
80
|
+
const validateDeployContext = () => {
|
|
81
|
+
if (process.env.NETLIFY && process.env.BUILD_ID) {
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
// workaround, Vercel does not have variables that are only accessible at build time
|
|
85
|
+
if (process.env.VERCEL && !process.env.VERCEL_REGION) {
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
88
|
+
if (process.env.SITECORE && process.env.BuildMetadata_BuildId) {
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
return false;
|
|
92
|
+
};
|
|
93
|
+
exports.validateDeployContext = validateDeployContext;
|
|
94
|
+
/**
|
|
95
|
+
* Parses the componentBuilder.ts file and returns a map of component names
|
|
96
|
+
* and their respective import strings
|
|
97
|
+
* @param {string} appPath path to the JSS app root
|
|
98
|
+
* @param {string} [componentMapPath] path to the app's component map file. Default: 'src/lib/componentMap.ts'
|
|
99
|
+
* @returns map of component names and their respective import strings
|
|
100
|
+
*/
|
|
101
|
+
const resolveComponentImportFiles = (appPath, componentMapPath = './src/lib/componentMap.ts') => {
|
|
102
|
+
appPath = path_1.default.isAbsolute(appPath) ? appPath : path_1.default.resolve(process.cwd(), appPath);
|
|
103
|
+
const tsConfig = ts.readConfigFile(path_1.default.resolve(appPath, 'tsconfig.json'), ts.sys.readFile);
|
|
104
|
+
if (tsConfig.error) {
|
|
105
|
+
throw new Error(`Error reading tsconfig.json from JSS app root: ${tsConfig.error.messageText}`);
|
|
106
|
+
}
|
|
107
|
+
const componentMapFullPath = path_1.default.isAbsolute(componentMapPath)
|
|
108
|
+
? componentMapPath
|
|
109
|
+
: path_1.default.resolve(appPath, componentMapPath);
|
|
110
|
+
const cliCompilerOptions = Object.assign(Object.assign({}, tsConfig.config.compilerOptions), { baseUrl: appPath });
|
|
111
|
+
const tsHost = ts.createCompilerHost(cliCompilerOptions, true);
|
|
112
|
+
const componentMapSourceFile = tsHost.getSourceFile(componentMapFullPath, ts.ScriptTarget.Latest, (msg) => {
|
|
113
|
+
throw new Error(`Failed to parse ${componentMapFullPath}: ${msg}`);
|
|
114
|
+
});
|
|
115
|
+
if (!componentMapSourceFile)
|
|
116
|
+
throw ReferenceError(`Failed to find file ${componentMapFullPath}`);
|
|
117
|
+
// this map matches all raw import strings (i.e. * as component) to import strings
|
|
118
|
+
const importStringsMap = {};
|
|
119
|
+
// this map will match component names only to full resolved source file paths
|
|
120
|
+
const componentImportsMap = new Map();
|
|
121
|
+
let mapExportName = '';
|
|
122
|
+
// all new xyz() statements in file
|
|
123
|
+
const newAssignments = [];
|
|
124
|
+
// all map.set() assignments in file
|
|
125
|
+
const mapAssignments = [];
|
|
126
|
+
// this function will traverse the map = new Map([/values/]) statement
|
|
127
|
+
// and get the component names registered in map from it
|
|
128
|
+
const traverseNewStatement = (node) => {
|
|
129
|
+
// going through map invocation, we're looking for outer array value
|
|
130
|
+
if (ts.isArrayLiteralExpression(node)) {
|
|
131
|
+
ts.forEachChild(node, (childNode) => {
|
|
132
|
+
// and then parse each individual array pair (i.e. ['MyComp', MyComp])
|
|
133
|
+
if (!ts.isArrayLiteralExpression(childNode)) {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
const componentKey = childNode.elements[1].getText();
|
|
137
|
+
const componentImport = Object.keys(importStringsMap).find((importStatement) => {
|
|
138
|
+
const matcher = new RegExp(`\\b(${componentKey})\\b`);
|
|
139
|
+
return importStatement.match(matcher) !== null;
|
|
140
|
+
});
|
|
141
|
+
if (componentImport) {
|
|
142
|
+
const componentValue = importStringsMap[componentImport];
|
|
143
|
+
componentImportsMap.set(componentKey, componentValue);
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
else if (node.getChildCount() > 0) {
|
|
148
|
+
ts.forEachChild(node, (childNode) => {
|
|
149
|
+
traverseNewStatement(childNode);
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
// step 1: get all import statements, map assignments (map.set) and map inits (map = new Map()) from componentMap file
|
|
154
|
+
ts.forEachChild(componentMapSourceFile, (childNode) => {
|
|
155
|
+
var _a;
|
|
156
|
+
// first, all import statements are parsed
|
|
157
|
+
if (ts.isImportDeclaration(childNode) && childNode.importClause) {
|
|
158
|
+
// import path is extracted
|
|
159
|
+
const moduleName = childNode.moduleSpecifier.getText().replace(/['"]/g, '');
|
|
160
|
+
// unless the import is a nodeJS one, or points to dependency package, resolve full path to the imported source file
|
|
161
|
+
if (moduleName.startsWith('node:') || moduleName.indexOf('/node_modules') > -1) {
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
const resolvedModule = ts.nodeModuleNameResolver(moduleName, componentMapFullPath, cliCompilerOptions, tsHost);
|
|
165
|
+
const resolvedFile = (_a = resolvedModule === null || resolvedModule === void 0 ? void 0 : resolvedModule.resolvedModule) === null || _a === void 0 ? void 0 : _a.resolvedFileName;
|
|
166
|
+
// module imports will be resolved to /node_modules location - we don't support that yet
|
|
167
|
+
if (resolvedFile && resolvedFile.indexOf('node_modules') === -1) {
|
|
168
|
+
importStringsMap[childNode.importClause.getText()] = path_1.default.resolve(resolvedFile);
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
console.warn('Could not resolve a file for import %s', moduleName);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
else if (ts.isExpressionStatement(childNode)) {
|
|
175
|
+
// parse map assignments (map.set(..)) to get registered components
|
|
176
|
+
ts.forEachChild(childNode, (expressionNode) => {
|
|
177
|
+
if (ts.isCallExpression(expressionNode) &&
|
|
178
|
+
expressionNode.expression.getText().indexOf('set') !== -1) {
|
|
179
|
+
// get map.set assignments
|
|
180
|
+
mapAssignments.push(expressionNode);
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
else if (ts.isExportAssignment(childNode)) {
|
|
185
|
+
// get component map export variable
|
|
186
|
+
mapExportName = childNode.expression.getText();
|
|
187
|
+
}
|
|
188
|
+
else if (childNode.kind === ts.SyntaxKind.FirstStatement) {
|
|
189
|
+
// get potential map = new Map() assignments to extract initial component values from
|
|
190
|
+
newAssignments.push(childNode);
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
// step 2: parse map assignments (from map.set and the new Map()) and retrieve import paths
|
|
194
|
+
// only for components registered into component map
|
|
195
|
+
for (const mapAssignment of newAssignments) {
|
|
196
|
+
// parse new Map() statement first
|
|
197
|
+
// only consider variable name for map that is exported
|
|
198
|
+
if (
|
|
199
|
+
// get the (maybe) exported new Map() statement
|
|
200
|
+
// matches i.e. export const map.. / export default const map.. / let map = .. / etc
|
|
201
|
+
mapAssignment
|
|
202
|
+
.getText()
|
|
203
|
+
.match(`^((export )|(export default ))?\\b(var|let|const)\\b\\s{1}\\b(${mapExportName})\\b`)) {
|
|
204
|
+
traverseNewStatement(mapAssignment);
|
|
205
|
+
break;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
for (const mapAssignment of mapAssignments) {
|
|
209
|
+
// only consider the map variable that is exported
|
|
210
|
+
if (mapAssignment.getText().startsWith(mapExportName)) {
|
|
211
|
+
const componentKey = mapAssignment.arguments[1].getText();
|
|
212
|
+
const componentImport = Object.keys(importStringsMap).find((importStatement) => {
|
|
213
|
+
const matcher = new RegExp(`\\b(${componentKey})\\b`);
|
|
214
|
+
return importStatement.match(matcher) !== null;
|
|
215
|
+
});
|
|
216
|
+
if (componentImport) {
|
|
217
|
+
const componentValue = importStringsMap[componentImport];
|
|
218
|
+
componentImportsMap.set(componentKey, componentValue);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
return componentImportsMap;
|
|
223
|
+
};
|
|
224
|
+
exports.resolveComponentImportFiles = resolveComponentImportFiles;
|
|
225
|
+
const sendCode = (_a) => __awaiter(void 0, [_a], void 0, function* ({ file, token, edgeUrl, }) {
|
|
226
|
+
const meshEndpoint = `${edgeUrl || core_1.constants.SITECORE_EDGE_URL_DEFAULT}/api/v1/mesh`;
|
|
227
|
+
if (!fs_1.default.existsSync(file.path)) {
|
|
228
|
+
console.error(chalk_1.default.red(`Component file not found: ${file.path}`));
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
const code = fs_1.default.readFileSync(file.path);
|
|
232
|
+
const response = yield fetch(meshEndpoint, {
|
|
233
|
+
method: 'POST',
|
|
234
|
+
headers: {
|
|
235
|
+
Authorization: `Bearer ${token}`,
|
|
236
|
+
'Content-Type': 'application/json',
|
|
237
|
+
},
|
|
238
|
+
body: JSON.stringify({
|
|
239
|
+
name: file.name,
|
|
240
|
+
content: code.toString(),
|
|
241
|
+
labels: {
|
|
242
|
+
properties: {
|
|
243
|
+
type: file.type,
|
|
244
|
+
},
|
|
245
|
+
},
|
|
246
|
+
}),
|
|
247
|
+
});
|
|
248
|
+
if (!response.ok) {
|
|
249
|
+
console.error(chalk_1.default.red(`Failed to send extracted code from ${file.path}: ${response.statusText}`));
|
|
250
|
+
}
|
|
251
|
+
else {
|
|
252
|
+
console.log(chalk_1.default.green(`Code from ${file.path} extracted and sent to mesh endpoint`));
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
exports.sendCode = sendCode;
|
package/dist/cjs/tools/index.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ModuleType = exports.generatePlugins = exports.getComponentList = exports.generateMetadata = exports.generateSites = void 0;
|
|
3
|
+
exports.extractComponents = exports.ModuleType = exports.generatePlugins = exports.getComponentList = exports.generateMetadata = exports.generateSites = void 0;
|
|
4
4
|
var tools_1 = require("@sitecore-content-sdk/core/tools");
|
|
5
5
|
Object.defineProperty(exports, "generateSites", { enumerable: true, get: function () { return tools_1.generateSites; } });
|
|
6
6
|
Object.defineProperty(exports, "generateMetadata", { enumerable: true, get: function () { return tools_1.generateMetadata; } });
|
|
7
7
|
Object.defineProperty(exports, "getComponentList", { enumerable: true, get: function () { return tools_1.getComponentList; } });
|
|
8
8
|
Object.defineProperty(exports, "generatePlugins", { enumerable: true, get: function () { return tools_1.generatePlugins; } });
|
|
9
9
|
Object.defineProperty(exports, "ModuleType", { enumerable: true, get: function () { return tools_1.ModuleType; } });
|
|
10
|
+
var extract_components_1 = require("./codegen/extract-components");
|
|
11
|
+
Object.defineProperty(exports, "extractComponents", { enumerable: true, get: function () { return extract_components_1.extractComponents; } });
|
|
@@ -10,9 +10,8 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
10
10
|
return t;
|
|
11
11
|
};
|
|
12
12
|
import React, { forwardRef } from 'react';
|
|
13
|
-
import PropTypes from 'prop-types';
|
|
14
13
|
import NextLink from 'next/link';
|
|
15
|
-
import { Link as ReactLink,
|
|
14
|
+
import { Link as ReactLink, } from '@sitecore-content-sdk/react';
|
|
16
15
|
/**
|
|
17
16
|
* Matches relative URLs that end with a file extension.
|
|
18
17
|
*/
|
|
@@ -51,4 +50,3 @@ export const Link = forwardRef((props, ref) => {
|
|
|
51
50
|
return (React.createElement(ReactLink, Object.assign({}, reactLinkProps, { ref: ref }, (process.env.TEST ? { 'data-react-link': true } : {}))));
|
|
52
51
|
});
|
|
53
52
|
Link.displayName = 'NextLink';
|
|
54
|
-
Link.propTypes = Object.assign({ internalLinkMatcher: PropTypes.instanceOf(RegExp) }, LinkPropTypes);
|
|
@@ -10,15 +10,12 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
10
10
|
return t;
|
|
11
11
|
};
|
|
12
12
|
import { mediaApi } from '@sitecore-content-sdk/core/media';
|
|
13
|
-
import PropTypes from 'prop-types';
|
|
14
13
|
import React from 'react';
|
|
15
|
-
import { withFieldMetadata, SitecoreContextReactContext, } from '@sitecore-content-sdk/react';
|
|
14
|
+
import { withFieldMetadata, SitecoreContextReactContext, DefaultEmptyFieldEditingComponentImage, withEmptyFieldEditingComponent, } from '@sitecore-content-sdk/react';
|
|
16
15
|
import Image from 'next/image';
|
|
17
|
-
import {
|
|
18
|
-
import { DefaultEmptyFieldEditingComponentImage } from '@sitecore-content-sdk/react';
|
|
19
|
-
import { isFieldValueEmpty, LayoutServicePageState } from '@sitecore-content-sdk/core/layout';
|
|
16
|
+
import { isFieldValueEmpty, LayoutServicePageState, RenderingType, } from '@sitecore-content-sdk/core/layout';
|
|
20
17
|
export const NextImage = withFieldMetadata(withEmptyFieldEditingComponent((_a) => {
|
|
21
|
-
var _b;
|
|
18
|
+
var _b, _c;
|
|
22
19
|
var { editable = true, imageParams, field, mediaUrlPrefix, fill, priority } = _a, otherProps = __rest(_a, ["editable", "imageParams", "field", "mediaUrlPrefix", "fill", "priority"]);
|
|
23
20
|
const sitecoreContext = React.useContext(SitecoreContextReactContext);
|
|
24
21
|
// next handles src and we use a custom loader,
|
|
@@ -37,9 +34,10 @@ export const NextImage = withFieldMetadata(withEmptyFieldEditingComponent((_a) =
|
|
|
37
34
|
if (!img) {
|
|
38
35
|
return null;
|
|
39
36
|
}
|
|
40
|
-
// disable image optimization for Edit
|
|
37
|
+
// disable image optimization for Edit / Preview / Component rendering, but preserve original value if true
|
|
41
38
|
const unoptimized = otherProps.unoptimized ||
|
|
42
|
-
((_b = sitecoreContext.context) === null || _b === void 0 ? void 0 : _b.
|
|
39
|
+
((_b = sitecoreContext.context) === null || _b === void 0 ? void 0 : _b.renderingType) === RenderingType.Component ||
|
|
40
|
+
((_c = sitecoreContext.context) === null || _c === void 0 ? void 0 : _c.pageState) !== LayoutServicePageState.Normal;
|
|
43
41
|
const attrs = Object.assign(Object.assign(Object.assign({}, img), otherProps), { fill,
|
|
44
42
|
priority, src: mediaApi.updateImageUrl(img.src, imageParams, mediaUrlPrefix), unoptimized });
|
|
45
43
|
const imageProps = Object.assign(Object.assign({}, attrs), {
|
|
@@ -56,21 +54,4 @@ export const NextImage = withFieldMetadata(withEmptyFieldEditingComponent((_a) =
|
|
|
56
54
|
}
|
|
57
55
|
return null; // we can't handle the truth
|
|
58
56
|
}, { defaultEmptyFieldEditingComponent: DefaultEmptyFieldEditingComponentImage }));
|
|
59
|
-
NextImage.propTypes = {
|
|
60
|
-
field: PropTypes.oneOfType([
|
|
61
|
-
PropTypes.shape({
|
|
62
|
-
src: PropTypes.string.isRequired,
|
|
63
|
-
}),
|
|
64
|
-
PropTypes.shape({
|
|
65
|
-
value: PropTypes.object,
|
|
66
|
-
}),
|
|
67
|
-
]),
|
|
68
|
-
editable: PropTypes.bool,
|
|
69
|
-
mediaUrlPrefix: PropTypes.instanceOf(RegExp),
|
|
70
|
-
imageParams: PropTypes.objectOf(PropTypes.oneOfType([PropTypes.number.isRequired, PropTypes.string.isRequired]).isRequired),
|
|
71
|
-
emptyFieldEditingComponent: PropTypes.oneOfType([
|
|
72
|
-
PropTypes.object,
|
|
73
|
-
PropTypes.func,
|
|
74
|
-
]),
|
|
75
|
-
};
|
|
76
57
|
NextImage.displayName = 'NextImage';
|
|
@@ -10,9 +10,8 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
10
10
|
return t;
|
|
11
11
|
};
|
|
12
12
|
import React, { useEffect, useRef } from 'react';
|
|
13
|
-
import PropTypes from 'prop-types';
|
|
14
13
|
import { useRouter } from 'next/router';
|
|
15
|
-
import { RichText as ReactRichText,
|
|
14
|
+
import { RichText as ReactRichText, } from '@sitecore-content-sdk/react';
|
|
16
15
|
export const prefetched = {};
|
|
17
16
|
export const RichText = (props) => {
|
|
18
17
|
const { internalLinksSelector = 'a[href^="/"]', prefetchLinks = true, editable = true } = props, rest = __rest(props, ["internalLinksSelector", "prefetchLinks", "editable"]);
|
|
@@ -63,5 +62,4 @@ export const RichText = (props) => {
|
|
|
63
62
|
};
|
|
64
63
|
return React.createElement(ReactRichText, Object.assign({ ref: richTextRef, editable: editable }, rest));
|
|
65
64
|
};
|
|
66
|
-
RichText.propTypes = Object.assign({ internalLinksSelector: PropTypes.string }, RichTextPropTypes);
|
|
67
65
|
RichText.displayName = 'NextRichText';
|
|
@@ -101,7 +101,7 @@ export class EditingRenderMiddleware extends RenderMiddlewareBase {
|
|
|
101
101
|
language: query.sc_lang,
|
|
102
102
|
site: query.sc_site,
|
|
103
103
|
mode: 'library',
|
|
104
|
-
dataSourceId: query.
|
|
104
|
+
dataSourceId: query.dataSourceId,
|
|
105
105
|
version: query.sc_version,
|
|
106
106
|
}, {
|
|
107
107
|
maxAge: 3,
|
|
@@ -236,9 +236,9 @@ export class RedirectsMiddleware extends MiddlewareBase {
|
|
|
236
236
|
return false;
|
|
237
237
|
})
|
|
238
238
|
.join('&');
|
|
239
|
-
const newUrl = new URL(`${url.pathname}?${newQueryString}`, url.origin);
|
|
239
|
+
const newUrl = new URL(`${url.pathname.toLowerCase()}?${newQueryString}`, url.origin);
|
|
240
240
|
url.search = newUrl.search;
|
|
241
|
-
url.pathname = newUrl.pathname;
|
|
241
|
+
url.pathname = newUrl.pathname.toLocaleLowerCase();
|
|
242
242
|
url.href = newUrl.href;
|
|
243
243
|
return url;
|
|
244
244
|
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import chalk from 'chalk';
|
|
11
|
+
import { ExtractedFileType, resolveComponentImportFiles, sendCode, validateConsent, validateDeployContext, } from './utils';
|
|
12
|
+
import { constants } from '@sitecore-content-sdk/core';
|
|
13
|
+
import { fetchBearerToken } from '@sitecore-content-sdk/core/tools';
|
|
14
|
+
/**
|
|
15
|
+
* Extracts components from the app folder and sends them to XMCloud.
|
|
16
|
+
* @param {ExtractComponentsConfig} args - Config for components extraction
|
|
17
|
+
*/
|
|
18
|
+
export const extractComponents = (args) => {
|
|
19
|
+
const authParams = {
|
|
20
|
+
clientId: process.env.SITECORE_AUTH_CLIENT_ID || '',
|
|
21
|
+
clientSecret: process.env.SITECORE_AUTH_CLIENT_SECRET || '',
|
|
22
|
+
endpoint: process.env.SITECORE_AUTH_ENDPOINT || constants.DEFAULT_SITECORE_AUTH_ENDPOINT,
|
|
23
|
+
audience: process.env.SITECORE_AUTH_AUDIENCE || constants.DEFAULT_SITECORE_AUTH_AUDIENCE,
|
|
24
|
+
};
|
|
25
|
+
return () => __awaiter(void 0, void 0, void 0, function* () {
|
|
26
|
+
if (!validateDeployContext()) {
|
|
27
|
+
console.log(chalk.yellow('Skipping code extraction, not in deploy context'));
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
if (!validateConsent()) {
|
|
31
|
+
console.log(chalk.yellow('Skipping code extraction, consent not given'));
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const basePath = process.cwd();
|
|
35
|
+
try {
|
|
36
|
+
const bearer = yield fetchBearerToken(authParams);
|
|
37
|
+
if (!bearer) {
|
|
38
|
+
console.error(chalk.red('Failed to get bearer token, aborting code extraction'));
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
const componentPaths = yield resolveComponentImportFiles(basePath, args.componentMapPath);
|
|
42
|
+
const codeDispatches = Array.from(componentPaths, (mapEntry) => sendCode({
|
|
43
|
+
file: {
|
|
44
|
+
name: mapEntry[0],
|
|
45
|
+
path: mapEntry[1],
|
|
46
|
+
type: ExtractedFileType.Component,
|
|
47
|
+
},
|
|
48
|
+
token: bearer,
|
|
49
|
+
edgeUrl: args.scConfig.api.edge.edgeUrl,
|
|
50
|
+
}));
|
|
51
|
+
yield Promise.all(codeDispatches);
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
console.error(chalk.red('Error during component extraction:', error));
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
};
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import chalk from 'chalk';
|
|
11
|
+
import path from 'path';
|
|
12
|
+
import fs from 'fs';
|
|
13
|
+
import * as ts from 'typescript';
|
|
14
|
+
import { constants } from '@sitecore-content-sdk/core';
|
|
15
|
+
/**
|
|
16
|
+
* Type of file to be sent to the mesh endpoint
|
|
17
|
+
*/
|
|
18
|
+
export var ExtractedFileType;
|
|
19
|
+
(function (ExtractedFileType) {
|
|
20
|
+
ExtractedFileType["Component"] = "component";
|
|
21
|
+
ExtractedFileType["Json"] = "json";
|
|
22
|
+
ExtractedFileType["Package"] = "package.json";
|
|
23
|
+
})(ExtractedFileType || (ExtractedFileType = {}));
|
|
24
|
+
/**
|
|
25
|
+
* Validates consent for code extraction procedures
|
|
26
|
+
* @returns {boolean} - true if consent is given, false otherwise
|
|
27
|
+
*/
|
|
28
|
+
export const validateConsent = () => {
|
|
29
|
+
if (!process.env.EXTRACT_CONSENT) {
|
|
30
|
+
console.log(chalk.yellow('EXTRACT_CONSENT is not set'));
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
return true;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Validates if the current operation is done in Vercel, Netlify or XMCloud
|
|
37
|
+
* deploy context
|
|
38
|
+
* @returns {boolean} - true if in deploy context, false otherwise
|
|
39
|
+
*/
|
|
40
|
+
export const validateDeployContext = () => {
|
|
41
|
+
if (process.env.NETLIFY && process.env.BUILD_ID) {
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
// workaround, Vercel does not have variables that are only accessible at build time
|
|
45
|
+
if (process.env.VERCEL && !process.env.VERCEL_REGION) {
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
if (process.env.SITECORE && process.env.BuildMetadata_BuildId) {
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
return false;
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* Parses the componentBuilder.ts file and returns a map of component names
|
|
55
|
+
* and their respective import strings
|
|
56
|
+
* @param {string} appPath path to the JSS app root
|
|
57
|
+
* @param {string} [componentMapPath] path to the app's component map file. Default: 'src/lib/componentMap.ts'
|
|
58
|
+
* @returns map of component names and their respective import strings
|
|
59
|
+
*/
|
|
60
|
+
export const resolveComponentImportFiles = (appPath, componentMapPath = './src/lib/componentMap.ts') => {
|
|
61
|
+
appPath = path.isAbsolute(appPath) ? appPath : path.resolve(process.cwd(), appPath);
|
|
62
|
+
const tsConfig = ts.readConfigFile(path.resolve(appPath, 'tsconfig.json'), ts.sys.readFile);
|
|
63
|
+
if (tsConfig.error) {
|
|
64
|
+
throw new Error(`Error reading tsconfig.json from JSS app root: ${tsConfig.error.messageText}`);
|
|
65
|
+
}
|
|
66
|
+
const componentMapFullPath = path.isAbsolute(componentMapPath)
|
|
67
|
+
? componentMapPath
|
|
68
|
+
: path.resolve(appPath, componentMapPath);
|
|
69
|
+
const cliCompilerOptions = Object.assign(Object.assign({}, tsConfig.config.compilerOptions), { baseUrl: appPath });
|
|
70
|
+
const tsHost = ts.createCompilerHost(cliCompilerOptions, true);
|
|
71
|
+
const componentMapSourceFile = tsHost.getSourceFile(componentMapFullPath, ts.ScriptTarget.Latest, (msg) => {
|
|
72
|
+
throw new Error(`Failed to parse ${componentMapFullPath}: ${msg}`);
|
|
73
|
+
});
|
|
74
|
+
if (!componentMapSourceFile)
|
|
75
|
+
throw ReferenceError(`Failed to find file ${componentMapFullPath}`);
|
|
76
|
+
// this map matches all raw import strings (i.e. * as component) to import strings
|
|
77
|
+
const importStringsMap = {};
|
|
78
|
+
// this map will match component names only to full resolved source file paths
|
|
79
|
+
const componentImportsMap = new Map();
|
|
80
|
+
let mapExportName = '';
|
|
81
|
+
// all new xyz() statements in file
|
|
82
|
+
const newAssignments = [];
|
|
83
|
+
// all map.set() assignments in file
|
|
84
|
+
const mapAssignments = [];
|
|
85
|
+
// this function will traverse the map = new Map([/values/]) statement
|
|
86
|
+
// and get the component names registered in map from it
|
|
87
|
+
const traverseNewStatement = (node) => {
|
|
88
|
+
// going through map invocation, we're looking for outer array value
|
|
89
|
+
if (ts.isArrayLiteralExpression(node)) {
|
|
90
|
+
ts.forEachChild(node, (childNode) => {
|
|
91
|
+
// and then parse each individual array pair (i.e. ['MyComp', MyComp])
|
|
92
|
+
if (!ts.isArrayLiteralExpression(childNode)) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
const componentKey = childNode.elements[1].getText();
|
|
96
|
+
const componentImport = Object.keys(importStringsMap).find((importStatement) => {
|
|
97
|
+
const matcher = new RegExp(`\\b(${componentKey})\\b`);
|
|
98
|
+
return importStatement.match(matcher) !== null;
|
|
99
|
+
});
|
|
100
|
+
if (componentImport) {
|
|
101
|
+
const componentValue = importStringsMap[componentImport];
|
|
102
|
+
componentImportsMap.set(componentKey, componentValue);
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
else if (node.getChildCount() > 0) {
|
|
107
|
+
ts.forEachChild(node, (childNode) => {
|
|
108
|
+
traverseNewStatement(childNode);
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
// step 1: get all import statements, map assignments (map.set) and map inits (map = new Map()) from componentMap file
|
|
113
|
+
ts.forEachChild(componentMapSourceFile, (childNode) => {
|
|
114
|
+
var _a;
|
|
115
|
+
// first, all import statements are parsed
|
|
116
|
+
if (ts.isImportDeclaration(childNode) && childNode.importClause) {
|
|
117
|
+
// import path is extracted
|
|
118
|
+
const moduleName = childNode.moduleSpecifier.getText().replace(/['"]/g, '');
|
|
119
|
+
// unless the import is a nodeJS one, or points to dependency package, resolve full path to the imported source file
|
|
120
|
+
if (moduleName.startsWith('node:') || moduleName.indexOf('/node_modules') > -1) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
const resolvedModule = ts.nodeModuleNameResolver(moduleName, componentMapFullPath, cliCompilerOptions, tsHost);
|
|
124
|
+
const resolvedFile = (_a = resolvedModule === null || resolvedModule === void 0 ? void 0 : resolvedModule.resolvedModule) === null || _a === void 0 ? void 0 : _a.resolvedFileName;
|
|
125
|
+
// module imports will be resolved to /node_modules location - we don't support that yet
|
|
126
|
+
if (resolvedFile && resolvedFile.indexOf('node_modules') === -1) {
|
|
127
|
+
importStringsMap[childNode.importClause.getText()] = path.resolve(resolvedFile);
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
console.warn('Could not resolve a file for import %s', moduleName);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
else if (ts.isExpressionStatement(childNode)) {
|
|
134
|
+
// parse map assignments (map.set(..)) to get registered components
|
|
135
|
+
ts.forEachChild(childNode, (expressionNode) => {
|
|
136
|
+
if (ts.isCallExpression(expressionNode) &&
|
|
137
|
+
expressionNode.expression.getText().indexOf('set') !== -1) {
|
|
138
|
+
// get map.set assignments
|
|
139
|
+
mapAssignments.push(expressionNode);
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
else if (ts.isExportAssignment(childNode)) {
|
|
144
|
+
// get component map export variable
|
|
145
|
+
mapExportName = childNode.expression.getText();
|
|
146
|
+
}
|
|
147
|
+
else if (childNode.kind === ts.SyntaxKind.FirstStatement) {
|
|
148
|
+
// get potential map = new Map() assignments to extract initial component values from
|
|
149
|
+
newAssignments.push(childNode);
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
// step 2: parse map assignments (from map.set and the new Map()) and retrieve import paths
|
|
153
|
+
// only for components registered into component map
|
|
154
|
+
for (const mapAssignment of newAssignments) {
|
|
155
|
+
// parse new Map() statement first
|
|
156
|
+
// only consider variable name for map that is exported
|
|
157
|
+
if (
|
|
158
|
+
// get the (maybe) exported new Map() statement
|
|
159
|
+
// matches i.e. export const map.. / export default const map.. / let map = .. / etc
|
|
160
|
+
mapAssignment
|
|
161
|
+
.getText()
|
|
162
|
+
.match(`^((export )|(export default ))?\\b(var|let|const)\\b\\s{1}\\b(${mapExportName})\\b`)) {
|
|
163
|
+
traverseNewStatement(mapAssignment);
|
|
164
|
+
break;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
for (const mapAssignment of mapAssignments) {
|
|
168
|
+
// only consider the map variable that is exported
|
|
169
|
+
if (mapAssignment.getText().startsWith(mapExportName)) {
|
|
170
|
+
const componentKey = mapAssignment.arguments[1].getText();
|
|
171
|
+
const componentImport = Object.keys(importStringsMap).find((importStatement) => {
|
|
172
|
+
const matcher = new RegExp(`\\b(${componentKey})\\b`);
|
|
173
|
+
return importStatement.match(matcher) !== null;
|
|
174
|
+
});
|
|
175
|
+
if (componentImport) {
|
|
176
|
+
const componentValue = importStringsMap[componentImport];
|
|
177
|
+
componentImportsMap.set(componentKey, componentValue);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return componentImportsMap;
|
|
182
|
+
};
|
|
183
|
+
export const sendCode = (_a) => __awaiter(void 0, [_a], void 0, function* ({ file, token, edgeUrl, }) {
|
|
184
|
+
const meshEndpoint = `${edgeUrl || constants.SITECORE_EDGE_URL_DEFAULT}/api/v1/mesh`;
|
|
185
|
+
if (!fs.existsSync(file.path)) {
|
|
186
|
+
console.error(chalk.red(`Component file not found: ${file.path}`));
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
const code = fs.readFileSync(file.path);
|
|
190
|
+
const response = yield fetch(meshEndpoint, {
|
|
191
|
+
method: 'POST',
|
|
192
|
+
headers: {
|
|
193
|
+
Authorization: `Bearer ${token}`,
|
|
194
|
+
'Content-Type': 'application/json',
|
|
195
|
+
},
|
|
196
|
+
body: JSON.stringify({
|
|
197
|
+
name: file.name,
|
|
198
|
+
content: code.toString(),
|
|
199
|
+
labels: {
|
|
200
|
+
properties: {
|
|
201
|
+
type: file.type,
|
|
202
|
+
},
|
|
203
|
+
},
|
|
204
|
+
}),
|
|
205
|
+
});
|
|
206
|
+
if (!response.ok) {
|
|
207
|
+
console.error(chalk.red(`Failed to send extracted code from ${file.path}: ${response.statusText}`));
|
|
208
|
+
}
|
|
209
|
+
else {
|
|
210
|
+
console.log(chalk.green(`Code from ${file.path} extracted and sent to mesh endpoint`));
|
|
211
|
+
}
|
|
212
|
+
});
|
package/dist/esm/tools/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sitecore-content-sdk/nextjs",
|
|
3
|
-
"version": "0.2.0-beta.
|
|
3
|
+
"version": "0.2.0-beta.6",
|
|
4
4
|
"main": "dist/cjs/index.js",
|
|
5
5
|
"module": "dist/esm/index.js",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -33,15 +33,14 @@
|
|
|
33
33
|
"@sitecore-cloudsdk/core": "^0.5.0",
|
|
34
34
|
"@sitecore-cloudsdk/personalize": "^0.5.0",
|
|
35
35
|
"@testing-library/dom": "^10.4.0",
|
|
36
|
-
"@testing-library/react": "^16.
|
|
36
|
+
"@testing-library/react": "^16.3.0",
|
|
37
37
|
"@types/chai": "^4.3.4",
|
|
38
38
|
"@types/chai-as-promised": "^7.1.5",
|
|
39
39
|
"@types/chai-string": "^1.4.2",
|
|
40
40
|
"@types/mocha": "^10.0.1",
|
|
41
41
|
"@types/node": "~22.9.0",
|
|
42
|
-
"@types/
|
|
43
|
-
"@types/react": "^
|
|
44
|
-
"@types/react-dom": "^18.0.10",
|
|
42
|
+
"@types/react": "^19.1.2",
|
|
43
|
+
"@types/react-dom": "^19.1.3",
|
|
45
44
|
"@types/sinon": "^10.0.13",
|
|
46
45
|
"@types/sinon-chai": "^3.2.9",
|
|
47
46
|
"chai": "^4.3.7",
|
|
@@ -51,15 +50,15 @@
|
|
|
51
50
|
"cross-fetch": "^4.1.0",
|
|
52
51
|
"del-cli": "^6.0.0",
|
|
53
52
|
"eslint": "^8.56.0",
|
|
54
|
-
"eslint-plugin-react": "^7.
|
|
53
|
+
"eslint-plugin-react": "^7.37.5",
|
|
55
54
|
"jsdom": "^26.0.0",
|
|
56
55
|
"mocha": "^11.1.0",
|
|
57
|
-
"next": "^
|
|
56
|
+
"next": "^15.3.1",
|
|
58
57
|
"nock": "14.0.0-beta.7",
|
|
59
58
|
"nyc": "^17.1.0",
|
|
60
59
|
"proxyquire": "^2.1.3",
|
|
61
|
-
"react": "^
|
|
62
|
-
"react-dom": "^
|
|
60
|
+
"react": "^19.0.0",
|
|
61
|
+
"react-dom": "^19.0.0",
|
|
63
62
|
"sinon": "^19.0.2",
|
|
64
63
|
"sinon-chai": "^3.7.0",
|
|
65
64
|
"ts-node": "^10.9.1",
|
|
@@ -69,23 +68,28 @@
|
|
|
69
68
|
"@sitecore-cloudsdk/core": "^0.5.0",
|
|
70
69
|
"@sitecore-cloudsdk/events": "^0.5.0",
|
|
71
70
|
"@sitecore-cloudsdk/personalize": "^0.5.0",
|
|
72
|
-
"next": "^
|
|
73
|
-
"react": "^
|
|
74
|
-
"react-dom": "^
|
|
71
|
+
"next": "^15.3.1",
|
|
72
|
+
"react": "^19.1.0",
|
|
73
|
+
"react-dom": "^19.1.0",
|
|
74
|
+
"typescript": "^5.4.0"
|
|
75
|
+
},
|
|
76
|
+
"peerDependenciesMeta": {
|
|
77
|
+
"typescript": {
|
|
78
|
+
"optional": true
|
|
79
|
+
}
|
|
75
80
|
},
|
|
76
81
|
"dependencies": {
|
|
77
82
|
"@babel/parser": "^7.26.10",
|
|
78
|
-
"@sitecore-content-sdk/core": "0.2.0-beta.
|
|
79
|
-
"@sitecore-content-sdk/react": "0.2.0-beta.
|
|
83
|
+
"@sitecore-content-sdk/core": "0.2.0-beta.6",
|
|
84
|
+
"@sitecore-content-sdk/react": "0.2.0-beta.6",
|
|
80
85
|
"@vercel/kv": "^3.0.0",
|
|
81
|
-
"prop-types": "^15.8.1",
|
|
82
86
|
"recast": "^0.23.11",
|
|
83
87
|
"regex-parser": "^2.3.0",
|
|
84
88
|
"sync-disk-cache": "^2.1.0"
|
|
85
89
|
},
|
|
86
90
|
"description": "",
|
|
87
91
|
"types": "types/index.d.ts",
|
|
88
|
-
"gitHead": "
|
|
92
|
+
"gitHead": "a56ee0c0a3897b5f9915329e20469fa427ee13a8",
|
|
89
93
|
"files": [
|
|
90
94
|
"dist",
|
|
91
95
|
"types",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { ReactNode } from 'react';
|
|
1
|
+
import React, { ReactNode, JSX } from 'react';
|
|
2
2
|
import { ComponentPropsCollection } from '../sharedTypes/component-props';
|
|
3
3
|
/**
|
|
4
4
|
* Component props context which we are using in order to store data fetched on components level (getStaticProps/getServerSideProps)
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import
|
|
2
|
-
import PropTypes from 'prop-types';
|
|
1
|
+
import { JSX } from 'react';
|
|
3
2
|
import { RichTextProps as ReactRichTextProps } from '@sitecore-content-sdk/react';
|
|
4
3
|
export type RichTextProps = ReactRichTextProps & {
|
|
5
4
|
/**
|
|
@@ -22,17 +21,5 @@ export declare const prefetched: {
|
|
|
22
21
|
};
|
|
23
22
|
export declare const RichText: {
|
|
24
23
|
(props: RichTextProps): JSX.Element;
|
|
25
|
-
propTypes: {
|
|
26
|
-
field: PropTypes.Requireable<PropTypes.InferProps<{
|
|
27
|
-
value: PropTypes.Requireable<string>;
|
|
28
|
-
metadata: PropTypes.Requireable<{
|
|
29
|
-
[x: string]: any;
|
|
30
|
-
}>;
|
|
31
|
-
}>>;
|
|
32
|
-
tag: PropTypes.Requireable<string>;
|
|
33
|
-
editable: PropTypes.Requireable<boolean>;
|
|
34
|
-
emptyFieldEditingComponent: PropTypes.Requireable<NonNullable<React.ComponentClass<unknown, any> | React.FC<unknown>>>;
|
|
35
|
-
internalLinksSelector: PropTypes.Requireable<string>;
|
|
36
|
-
};
|
|
37
24
|
displayName: string;
|
|
38
25
|
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { SitecoreConfig } from '@sitecore-content-sdk/core/config';
|
|
2
|
+
export type ExtractComponentsConfig = {
|
|
3
|
+
scConfig: SitecoreConfig;
|
|
4
|
+
componentMapPath?: string;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* Extracts components from the app folder and sends them to XMCloud.
|
|
8
|
+
* @param {ExtractComponentsConfig} args - Config for components extraction
|
|
9
|
+
*/
|
|
10
|
+
export declare const extractComponents: (args: ExtractComponentsConfig) => () => Promise<void>;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Description properties for the files sent to the mesh endpoint
|
|
3
|
+
*/
|
|
4
|
+
export type ExtractedFile = {
|
|
5
|
+
name: string;
|
|
6
|
+
path: string;
|
|
7
|
+
type: ExtractedFileType;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Type of file to be sent to the mesh endpoint
|
|
11
|
+
*/
|
|
12
|
+
export declare enum ExtractedFileType {
|
|
13
|
+
Component = "component",
|
|
14
|
+
Json = "json",
|
|
15
|
+
Package = "package.json"
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Validates consent for code extraction procedures
|
|
19
|
+
* @returns {boolean} - true if consent is given, false otherwise
|
|
20
|
+
*/
|
|
21
|
+
export declare const validateConsent: () => boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Validates if the current operation is done in Vercel, Netlify or XMCloud
|
|
24
|
+
* deploy context
|
|
25
|
+
* @returns {boolean} - true if in deploy context, false otherwise
|
|
26
|
+
*/
|
|
27
|
+
export declare const validateDeployContext: () => boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Parses the componentBuilder.ts file and returns a map of component names
|
|
30
|
+
* and their respective import strings
|
|
31
|
+
* @param {string} appPath path to the JSS app root
|
|
32
|
+
* @param {string} [componentMapPath] path to the app's component map file. Default: 'src/lib/componentMap.ts'
|
|
33
|
+
* @returns map of component names and their respective import strings
|
|
34
|
+
*/
|
|
35
|
+
export declare const resolveComponentImportFiles: (appPath: string, componentMapPath?: string) => Map<string, string>;
|
|
36
|
+
export declare const sendCode: ({ file, token, edgeUrl, }: {
|
|
37
|
+
file: ExtractedFile;
|
|
38
|
+
token: string;
|
|
39
|
+
edgeUrl?: string;
|
|
40
|
+
}) => Promise<void>;
|
package/types/tools/index.d.ts
CHANGED