Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: merge VLE 1.2.0 branch #565

Open
wants to merge 24 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
529ee24
feat(scaffold): add prompts for scaffold template (#529)
asanehisa Jul 24, 2024
b239cbb
feat(dev): add pageSets to global variable in header in dev mode (#531)
jwartofsky-yext Jul 30, 2024
4039f5b
feat(scaffold): create ve template on scaffold (#532)
asanehisa Jul 30, 2024
0008e58
feat(scaffold): create dynamic and static templates on scaffold (#533)
asanehisa Jul 30, 2024
f7b7e7e
feat(dev): override document visual configuration (#534)
briantstephan Jul 31, 2024
dfb0452
release: [email protected]
mkilpatrick Aug 1, 2024
2953ca7
release: [email protected]
mkilpatrick Aug 1, 2024
1bbc72f
release: [email protected]
mkilpatrick Aug 1, 2024
bbdc78e
fix: reformat readme (#535)
brian-baugher Aug 12, 2024
4b63eb6
fix(scaffold): wording of scaffold template prompt (#537)
asanehisa Aug 12, 2024
cf43a28
fix(scaffold): update scaffold template command (#538)
briantstephan Aug 14, 2024
a8b5fc9
release: [email protected]
mkilpatrick Aug 15, 2024
e796621
fix(scaffold): edit puck version (#539)
asanehisa Aug 15, 2024
ad25864
release: [email protected]
mkilpatrick Aug 15, 2024
e3aa48d
fix(scaffold): remove isDraft from template (#540)
asanehisa Aug 16, 2024
8e575d4
fix(scaffold): remove eslint ignore (#541)
asanehisa Aug 21, 2024
d626be4
feat(dev): make site stream available to Visual Editor via global var…
benlife5 Aug 28, 2024
0020b15
fix(scaffold): puckConfigs -> componentRegistry (#543)
asanehisa Aug 29, 2024
9de7e31
release: [email protected]
mkilpatrick Aug 29, 2024
9574613
Merge remote-tracking branch 'origin' into 1.2.0
mkilpatrick Jan 2, 2025
db9d720
release: [email protected]
mkilpatrick Jan 2, 2025
58ee9bb
Merge remote-tracking branch 'origin' into 1.2.0
mkilpatrick Jan 14, 2025
4fe7b8e
fix(plugin): ignore templates marked for in platform page sets (#564)
benlife5 Jan 17, 2025
79a3d6b
fix(scaffold): update VE template generation (#563)
mkilpatrick Jan 21, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
PagesJS is a collection of tools that make it easy to develop on [Yext Pages](https://www.yext.com/platform/pages). It provides 2 main tools:

1. A default development server, backed by [Vite](https://vitejs.dev/), that makes local development fast and easy.
1. A Vite plugin used to bundle your assets and templates for Yext Pages.
2. A Vite plugin used to bundle your assets and templates for Yext Pages.

## Packages

Expand All @@ -17,11 +17,11 @@ PagesJS is a collection of tools that make it easy to develop on [Yext Pages](ht
## Utility Functions

| Function |
| -------------------------------------------------------------------------------------------------------- | --- |
| -------------------------------------------------------------------------------------------------------- |
| [fetch()](https://github.com/yext/pages/blob/main/packages/pages/src/util/README.md#fetch) |
| [getRuntime()](https://github.com/yext/pages/blob/main/packages/pages/src/util/README.md#getRuntime) |
| [isProduction()](https://github.com/yext/pages/blob/main/packages/pages/src/util/README.md#isProduction) |
| [useDocument()](https://github.com/yext/pages/blob/main/packages/pages/src/util/README.md#useDocument) | |
| [useDocument()](https://github.com/yext/pages/blob/main/packages/pages/src/util/README.md#useDocument) |

## Development

Expand Down
11 changes: 11 additions & 0 deletions packages/pages/etc/pages.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,17 @@ export interface TemplateConfig {
streamId?: string;
}

// @internal
export interface TemplateManifest {
templates: {
name: string;
description: string;
exampleSiteUrl: string;
layoutRequired: boolean;
defaultLayoutData: string;
}[];
}

// @public
export interface TemplateModule<
T extends TemplateProps,
Expand Down
124 changes: 122 additions & 2 deletions packages/pages/src/common/src/feature/stream.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
import { describe, it, expect } from "vitest";
import { convertTemplateConfigToStreamConfig, StreamConfig } from "./stream.js";
import { describe, it, expect, vi, afterEach } from "vitest";
import {
convertTemplateConfigToStreamConfig,
StreamConfig,
formatSiteStream,
readSiteStream,
} from "./stream.js";
import { TemplateConfigInternal } from "../template/internal/types.js";
import fs from "fs";
import { ProjectStructure } from "../project/structure.js";

describe("stream", () => {
it("returns void if no stream", async () => {
Expand Down Expand Up @@ -45,3 +52,116 @@ describe("stream", () => {
expect(streamConfig).toEqual(expectedStreamConfig);
});
});

const siteStreamPath = "foo/bar";

describe("formatSiteStream", () => {
it("errors and exits when there are multiple entityIds", () => {
const testJson = {
$id: "site-stream",
filter: { entityIds: ["1234", "123"] },
localization: { locales: ["en"] },
fields: [],
};
const mockExit = vi
.spyOn(process, "exit")
.mockImplementation(() => undefined as never);
formatSiteStream(testJson, siteStreamPath);
expect(mockExit).toHaveBeenCalledWith(1);
});

it("returns expected entityId", () => {
const testJson = {
$id: "my-site-stream",
filter: { entityIds: ["1234"] },
localization: { locales: ["en"] },
fields: [],
};
const expectedJson = {
id: "my-site-stream",
entityId: "1234",
localization: { locales: ["en"] },
fields: [],
};
expect(formatSiteStream(testJson, siteStreamPath)).toEqual(expectedJson);
});

it("returns expected full config", () => {
const testJson = {
$id: "site-stream-123",
fields: ["meta", "name"],
filter: { entityIds: ["1234"] },
source: "foo",
localization: { locales: ["en"] },
};
const expectedJson = {
id: "site-stream-123",
entityId: "1234",
fields: ["meta", "name"],
localization: { locales: ["en"] },
};
expect(formatSiteStream(testJson, siteStreamPath)).toEqual(expectedJson);
});
});

describe("readSiteStream", () => {
afterEach(() => {
if (fs.existsSync("config.yaml")) {
fs.rmSync("config.yaml");
}
if (fs.existsSync("sites-config/site-stream.json")) {
fs.rmSync("sites-config", { recursive: true, force: true });
}
});

const projectStructure = new ProjectStructure({});

it("reads siteStream from config.yaml", () => {
const path = "config.yaml";
fs.writeFileSync(
path,
`siteStream:
id: site-stream
entityId: site-stream-from-yaml
fields:
- c_visualLayouts.c_visualConfiguration
localization:
locales:
- en
`
);
const siteStream = readSiteStream(projectStructure);
expect(siteStream).toEqual({
id: "site-stream",
entityId: "site-stream-from-yaml",
fields: ["c_visualLayouts.c_visualConfiguration"],
localization: { locales: ["en"] },
});
});

it("reads siteStream from sites-config/sites-stream.json", () => {
projectStructure.getSitesConfigPath;
const path = "sites-config/site-stream.json";
fs.mkdirSync("sites-config");
fs.writeFileSync(
path,
`
{
"$id": "site-stream",
"filter": {
"entityIds": ["site-stream-from-json"]
},
"fields": ["c_visualLayouts.c_visualConfiguration"],
"localization": {"locales": ["en"]}
}
`
);
const siteStream = readSiteStream(projectStructure);
expect(siteStream).toEqual({
id: "site-stream",
entityId: "site-stream-from-json",
fields: ["c_visualLayouts.c_visualConfiguration"],
localization: { locales: ["en"] },
});
});
});
93 changes: 93 additions & 0 deletions packages/pages/src/common/src/feature/stream.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
import fs from "fs";
import path from "path";
import YAML from "yaml";

import { readJsonSync } from "../../../upgrade/migrateConfig.js";
import { TemplateConfigInternal } from "../template/internal/types.js";
import { RedirectConfigInternal } from "../redirect/internal/types.js";
import { ProjectStructure } from "../project/structure.js";
import { logErrorAndExit } from "../../../util/logError.js";
import { Stream } from "../template/types.js";

/**
* The shape of data that represents a stream configuration.
Expand Down Expand Up @@ -39,6 +46,38 @@ export interface StreamConfig {
};
}

/**
* The shape of data that represents a site stream.
* Similar to {@link StreamConfig} but there can only be one entityId.
*/
export interface SiteStream {
/** Identifies the stream */
id: string;
/** The entity id of the site stream */
entityId: string;
/** The fields to apply to the stream */
fields: string[];
/** The localization used by the filter. Either set primary: true or specify a locales array. */
localization:
| {
/** The entity profiles languages to apply to the stream. */
locales: string[];
primary?: never;
}
| {
/** Use the primary profile language. */
primary: true;
locales?: never;
};
/** The transformation to apply to the stream */
transform?: {
/** The option fields to be expanded to include the display fields, numeric values, and selected boolean */
expandOptionFields?: string[];
/** The option fields to be replaced with display names */
replaceOptionValuesWithDisplayNames?: string[];
};
}

/**
* Converts a {@link TemplateConfig.config.stream} into a valid {@link StreamConfig}.
*/
Expand Down Expand Up @@ -85,3 +124,57 @@ export const convertRedirectConfigToStreamConfig = (
};
}
};

/**
* Loads the site stream specified in config.yaml or site-stream.json into a {@link SiteStream}.
*/
export const readSiteStream = (
projectStructure: ProjectStructure
): SiteStream | undefined => {
// read site stream from deprecated sites-config directory if it exists
const siteStreamJsonPath = path.resolve(
projectStructure.getSitesConfigPath().path,
projectStructure.config.sitesConfigFiles.siteStream
);
if (fs.existsSync(siteStreamJsonPath)) {
const sitesJson = readJsonSync(siteStreamJsonPath);
return formatSiteStream(sitesJson, siteStreamJsonPath);
}

// read site stream from config.yaml
const configYamlPath = projectStructure.getConfigYamlPath().getAbsolutePath();
if (fs.existsSync(configYamlPath)) {
const yamlDoc = YAML.parse(fs.readFileSync(configYamlPath, "utf-8"));
if (yamlDoc.siteStream) {
yamlDoc.siteStream.entityId = yamlDoc.siteStream?.entityId?.toString();
return yamlDoc.siteStream;
}
}

return;
};

/**
* Converts the deprecated format of a siteStream specified in site-stream.json into
* the format of a siteStream specified in config.yaml
*/
export const formatSiteStream = (
sitesJson: Stream,
siteStreamPath: string
): SiteStream => {
let entityId;
if (sitesJson.filter?.entityIds && sitesJson.filter?.entityIds.length === 1) {
entityId = sitesJson.filter.entityIds[0];
} else if (sitesJson.filter?.entityIds) {
logErrorAndExit(
`Unable to migrate ${siteStreamPath} due to multiple entityIds`
);
}

return {
id: sitesJson.$id, // Replace $id with id and keeps id in the first position
entityId: entityId?.toString() || "",
localization: sitesJson.localization,
fields: sitesJson.fields,
};
};
32 changes: 32 additions & 0 deletions packages/pages/src/common/src/parsers/puckConfigParser.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { describe, it, expect } from "vitest";
import fs from "node:fs";
import { addDataToPuckConfig } from "./puckConfigParser.js";

describe("addDataToPuckConfig", () => {
it("should throw an error if the filepath is invalid", () => {
expect(() => addDataToPuckConfig("fileName", "invalid/filepath")).toThrow(
'Filepath "invalid/filepath" is invalid.'
);
});

it("correctly adds new config to the puck config file", () => {
try {
fs.writeFileSync(
"test.tsx",
`export const componentRegistry = new Map<string, Config<any>>([
["location", locationConfig],
]);`
);
addDataToPuckConfig("foo", "test.tsx");
const modifiedContent = fs.readFileSync("test.tsx", "utf-8");
expect(modifiedContent).toContain('["foo", fooConfig]');
expect(modifiedContent).toContain(
`export const fooConfig: Config<FooProps>`
);
} finally {
if (fs.existsSync("test.tsx")) {
fs.unlinkSync("test.tsx");
}
}
});
});
44 changes: 44 additions & 0 deletions packages/pages/src/common/src/parsers/puckConfigParser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import fs from "node:fs";
import SourceFileParser, { createTsMorphProject } from "./sourceFileParser.js";
import { newConfig } from "../../../scaffold/template/sampleTemplates.js";
import { SyntaxKind } from "ts-morph";

/**
* Adds variables to the puck config file and adds the new config to
* the exported map.
* @param fileName template name with invalid chars and spaces removed
* @param filepath /src/ve.config.tsx
*/
export function addDataToPuckConfig(fileName: string, filepath: string) {
if (!fs.existsSync(filepath)) {
throw new Error(`Filepath "${filepath}" is invalid.`);
}
const parser = new SourceFileParser(filepath, createTsMorphProject());

const puckConfigsStatement = parser.getVariableStatement("componentRegistry");

const formattedTemplateName =
fileName.charAt(0).toUpperCase() + fileName.slice(1);

const puckConfigsStartLocation = puckConfigsStatement.getStart();
parser.insertStatement(
newConfig(formattedTemplateName, fileName),
puckConfigsStartLocation
);

const puckConfigsDeclaration =
parser.getVariableDeclaration("componentRegistry");
const puckConfigsInitializer = puckConfigsDeclaration.getInitializer();
if (
puckConfigsInitializer &&
puckConfigsInitializer.getKind() === SyntaxKind.NewExpression
) {
const newExpression = puckConfigsInitializer;
const puckConfigsArray = newExpression.getFirstChildByKindOrThrow(
SyntaxKind.ArrayLiteralExpression
);
puckConfigsArray.addElement(`["${fileName}", ${fileName}Config]`);
}
parser.format();
parser.save();
}
Loading
Loading