Skip to content

Commit

Permalink
bug fixes and use config.yaml
Browse files Browse the repository at this point in the history
  • Loading branch information
mkilpatrick committed Aug 17, 2023
1 parent 8b85146 commit 31fa86f
Show file tree
Hide file tree
Showing 34 changed files with 289 additions and 211 deletions.
2 changes: 2 additions & 0 deletions packages/pages/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
"get-port": "^7.0.0",
"glob": "^10.3.3",
"handlebars": "^4.7.8",
"js-yaml": "^4.1.0",
"lodash": "^4.17.21",
"mime-types": "^2.1.35",
"module-from-string": "^3.3.0",
Expand All @@ -89,6 +90,7 @@
"@types/fs-extra": "^11.0.1",
"@types/glob": "^8.1.0",
"@types/jest": "^29.5.3",
"@types/js-yaml": "4.0.5",
"@types/lodash": "^4.14.197",
"@types/mime-types": "^2.1.1",
"@types/minimatch": "^5.1.2",
Expand Down
4 changes: 2 additions & 2 deletions packages/pages/src/build/build.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { Command } from "commander";
import { build } from "vite";

const handler = async (scope: string) => {
const handler = async ({ scope }: { scope: string }) => {
// Pass CLI arguments as env variables to use in vite-plugin
if (scope) {
process.env.YEXT_PAGES_SCOPE = scope;
}
build();
await build();
};

export const buildCommand = (program: Command) => {
Expand Down
10 changes: 5 additions & 5 deletions packages/pages/src/common/src/assets/getAssetsFilepath.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,17 @@ describe("getAssetsFilepath - determineAssetsFilepath", () => {
expect(actual).toEqual("assets");
});

it("returns custom assets from serving.json", async () => {
it("returns custom assetsDir from config.yaml", async () => {
const actual = await determineAssetsFilepath(
"assets",
"tests/fixtures/serving.json",
"tests/fixtures/config.yaml",
"tests/fixtures/vite.config.js"
);

expect(actual).toEqual("servingJsonAssetsDir");
expect(actual).toEqual("subpath/assets");
});

it("returns custom assets from vite config when no serving.json", async () => {
it("returns custom assets from vite config when no config.yaml", async () => {
const viteConfig = {
default: {
plugins: [],
Expand All @@ -33,7 +33,7 @@ describe("getAssetsFilepath - determineAssetsFilepath", () => {

const actual = await determineAssetsFilepath(
"assets",
"tests/fixtures/invalid.json",
"tests/fixtures/invalid.yaml",
"does not matter since mocked"
);

Expand Down
29 changes: 11 additions & 18 deletions packages/pages/src/common/src/assets/getAssetsFilepath.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,41 +2,34 @@ import fs from "node:fs";
import { pathToFileURL } from "url";
import { UserConfig } from "vite";
import { import_ } from "./import.js";
import yaml from "js-yaml";
import { ConfigYaml } from "../config/config.js";

/**
* Determines the assets directory to use by checking the following, in order:
* TODO: Check the config.yaml first once support is added
* 1. site-config/serving.json
* 1. config.yaml
* 2. vite.config.json assetDir
* 3. default to "assets"
* @param servingJsonPath the path to sites-config/serving.json - make sure to take scope into account
* @param viteConfigPath the path to vite.config.js
*/
export const determineAssetsFilepath = async (
defaultAssetsDir: string,
servingJsonPath: string,
configYamlPath: string,
viteConfigPath: string
) => {
if (servingJsonPath === "" || viteConfigPath === "") {
if (configYamlPath === "" || viteConfigPath === "") {
return defaultAssetsDir;
}

if (fs.existsSync(servingJsonPath)) {
const servingJson = JSON.parse(fs.readFileSync(servingJsonPath).toString());
const displayUrlPrefix = servingJson.displayUrlPrefix as string;
if (fs.existsSync(configYamlPath)) {
const configYaml = yaml.load(
fs.readFileSync(configYamlPath, "utf-8")
) as ConfigYaml;

if (displayUrlPrefix === "" || !displayUrlPrefix.includes("/")) {
return defaultAssetsDir;
if (configYaml.assetsDir && configYaml.assetsDir !== "") {
return configYaml.assetsDir;
}

const displayUrlPrefixParts = displayUrlPrefix.split("/");
displayUrlPrefixParts.shift(); // remove the hostname

if (displayUrlPrefixParts.length > 0) {
return displayUrlPrefixParts.join("/");
}

return defaultAssetsDir;
}

const viteConfig = await import_(pathToFileURL(viteConfigPath).toString());
Expand Down
7 changes: 7 additions & 0 deletions packages/pages/src/common/src/config/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export interface ConfigYaml {
/**
* The folder path that assets will be served from. If your using a reverse proxy at
* a subpath, this should typically look like "mySubpath/assets".
* */
assetsDir?: string;
}
10 changes: 2 additions & 8 deletions packages/pages/src/common/src/feature/features.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,7 @@ import { TemplateConfigInternal } from "../template/internal/types.js";
import { ProjectStructure } from "../project/structure.js";

describe("features - convertTemplateConfigToFeaturesConfig", () => {
let projectStructure: ProjectStructure;
beforeAll(async () => {
projectStructure = await ProjectStructure.init();
});
const projectStructure = new ProjectStructure();

it("returns a FeaturesConfig with no StreamConfig if no stream is defined", async () => {
const templateConfig: TemplateConfigInternal = {
Expand Down Expand Up @@ -86,10 +83,7 @@ describe("features - convertTemplateConfigToFeaturesConfig", () => {
});

describe("features - convertTemplateConfigToFeatureConfig", () => {
let projectStructure: ProjectStructure;
beforeAll(async () => {
projectStructure = await ProjectStructure.init();
});
const projectStructure = new ProjectStructure();

it("uses the streamId if defined and return an EntityPageSetConfig", async () => {
const templateConfig: TemplateConfigInternal = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,7 @@ jest.mock("vite", () => {
afterAll(() => jest.unmock("url"));

describe("loadTemplateModules", () => {
let projectStructure: ProjectStructure;
beforeAll(async () => {
projectStructure = await ProjectStructure.init();
});
const projectStructure = new ProjectStructure();

it("loads and transpiles raw templates", async () => {
const functionFile: path.ParsedPath[] = [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,7 @@ const createMockFilePath = (filepath: string): path.ParsedPath => {
};

describe("internal/types - convertFunctionModuleToFunctionModuleInternal", () => {
let projectStructure: ProjectStructure;
beforeAll(async () => {
projectStructure = await ProjectStructure.init();
});
const projectStructure = new ProjectStructure();

it("converts a function in functions/http", async () => {
const functionModule: FunctionModule = {
Expand Down
28 changes: 20 additions & 8 deletions packages/pages/src/common/src/project/structure.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,14 @@ export interface SitesConfigFiles {
serving: string;
}

/**
* Important files at the project's root.
*/
export interface RootFiles {
/** The config.yaml file */
config: string;
}

/**
* Defines how environment variables will be declared and processed.
*
Expand Down Expand Up @@ -85,6 +93,9 @@ export interface ProjectStructureConfig {
subfolders: Subfolders;
/** Files specific to the Yext configuration folder */
sitesConfigFiles: SitesConfigFiles;
/** Important files at the project's root */
rootFiles: RootFiles;
/** Defines how environment variables will be declared and processed */
envVarConfig: EnvVar;
/**
* This is used for the case of multibrand setup within a single repo.
Expand Down Expand Up @@ -120,6 +131,9 @@ const defaultProjectStructureConfig: ProjectStructureConfig = {
siteStream: "site-stream.json",
serving: "serving.json",
},
rootFiles: {
config: "config.yaml",
},
envVarConfig: {
envVarDir: "",
envVarPrefix: "YEXT_PUBLIC",
Expand All @@ -143,6 +157,11 @@ export type Optional<T> = {
export class ProjectStructure {
config: ProjectStructureConfig;

constructor(config?: Optional<ProjectStructureConfig>) {
const mergedConfig = merge(defaultProjectStructureConfig, config);
this.config = mergedConfig;
}

static init = async (
projectStructureConfig?: Optional<ProjectStructureConfig>
) => {
Expand All @@ -153,10 +172,7 @@ export class ProjectStructure {
? DEFAULT_ASSETS_DIR
: await determineAssetsFilepath(
DEFAULT_ASSETS_DIR,
pathLib.resolve(
config.rootFolders.sitesConfig,
config.sitesConfigFiles.serving
),
pathLib.resolve(config.rootFiles.config),
pathLib.resolve("vite.config.js")
); // TODO: handle other extensions

Expand All @@ -165,10 +181,6 @@ export class ProjectStructure {
return new ProjectStructure(config);
};

private constructor(config: ProjectStructureConfig) {
this.config = config;
}

/**
* @returns the list of of src/templates, taking scope into account. If a scope is defined then
* both the scoped and non-scoped template paths are returned.
Expand Down
15 changes: 5 additions & 10 deletions packages/pages/src/common/src/template/hydration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,10 @@ export const getHydrationTemplate = (
props: TemplateRenderProps
): string => {
return `
const componentUrl = import.meta.resolve("./${convertToPosixPath(
const componentUrl = import.meta.resolve("/${convertToPosixPath(
templateModulePath
)}");
const renderUrl = import.meta.resolve("./${convertToPosixPath(
const renderUrl = import.meta.resolve("/${convertToPosixPath(
clientRenderTemplatePath
)}");
Expand Down Expand Up @@ -160,7 +160,6 @@ export const getServerTemplatePlugin = (
serverHtml: string,
templateFilepath: string,
bundlerManifest: bundlerManifest,
relativePrefixToRoot: string,
appLanguage: string,
headConfig?: HeadConfig
) => {
Expand All @@ -170,10 +169,7 @@ export const getServerTemplatePlugin = (
appLanguage,
headConfig
);
html = injectIntoHead(
html,
getCssHtml(templateFilepath, bundlerManifest, relativePrefixToRoot)
);
html = injectIntoHead(html, getCssHtml(templateFilepath, bundlerManifest));

return html;
};
Expand All @@ -190,11 +186,10 @@ type ManifestInfo = {

const getCssHtml = (
templateFilepath: string,
bundlerManifest: bundlerManifest,
relativePrefixToRoot: string
bundlerManifest: bundlerManifest
): string => {
return Array.from(getCssTags(templateFilepath, bundlerManifest, new Set()))
.map((f) => `<link rel="stylesheet" href="${relativePrefixToRoot + f}"/>`)
.map((f) => `<link rel="stylesheet" href="/${f}"/>`)
.join("\n");
};

Expand Down
14 changes: 3 additions & 11 deletions packages/pages/src/common/src/template/types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { ProjectStructureConfig } from "../project/structure.js";
import { HeadConfig } from "./head.js";

/**
Expand Down Expand Up @@ -153,17 +154,8 @@ export type Manifest = {
renderPaths: {
[key: string]: string;
};
/** A map of project roots to their paths */
projectFilepaths: {
/** The folder path where all template files live */
templatesRoot: string;
/** The folder path where the compiled files live */
distRoot: string;
/** The folder path where the compiled server bundles live */
serverBundleOutputRoot: string;
/** The folder path where a subset of template files use for the build live */
scopedTemplatesPath?: string;
};
/** The configuration structure of a project */
projectStructure: ProjectStructureConfig;
/** If the bundler used generates a manifest.json then this field will contain that json object */
bundlerManifest?: any;
};
Expand Down
55 changes: 32 additions & 23 deletions packages/pages/src/dev/server/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,31 +29,10 @@ export const createServer = async (
// creates a standard express app
const app = express();

// initialize the default project structure and use to help configure the
// dev server
// initialize the default project structure and use to help configure the dev server
const projectStructure = await ProjectStructure.init({ scope });

// Read features.json and set the default locale to the first locale listed
// Default to en if features.json cannot be read or there is no locales entry
let defaultLocale = "en";
try {
const featuresJson = JSON.parse(
fs.readFileSync(
path.resolve(
projectStructure.getSitesConfigPath().path,
projectStructure.config.sitesConfigFiles.features
),
"utf-8"
)
);
if (featuresJson.locales && featuresJson.locales.length > 0) {
defaultLocale = featuresJson.locales[0];
}
} catch (e) {
if (e !== "Error: ENOENT: no such file or directory") {
console.warn(e);
}
}
const defaultLocale = getDefaultLocale(projectStructure);

// create vite using ssr mode
const vite = await createViteServer({
Expand All @@ -70,6 +49,7 @@ export const createServer = async (
include: ["react-dom", "react-dom/client"],
},
});

// register vite's middleware
app.use(vite.middlewares);

Expand Down Expand Up @@ -191,3 +171,32 @@ export const createServer = async (
process.stdout.write(`listening on :${devServerPort}\n`)
);
};

/**
* Read features.json and set the default locale to the first locale listed
* Default to en if features.json cannot be read or there is no locales entry
*/
const getDefaultLocale = (projectStructure: ProjectStructure) => {
// Read features.json and set the default locale to the first locale listed
// Default to en if features.json cannot be read or there is no locales entry
try {
const featuresJson = JSON.parse(
fs.readFileSync(
path.resolve(
projectStructure.getSitesConfigPath().path,
projectStructure.config.sitesConfigFiles.features
),
"utf-8"
)
);
if (featuresJson.locales && featuresJson.locales.length > 0) {
return featuresJson.locales[0];
}
} catch (e) {
if (e !== "Error: ENOENT: no such file or directory") {
console.warn(e);
}
}

return "en";
};
5 changes: 1 addition & 4 deletions packages/pages/src/dev/server/ssr/generateTestData.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,7 @@ jest.mock("child_process", () => ({
}));

describe("generateTestDataForPage", () => {
let projectStructure: ProjectStructure;
beforeAll(async () => {
projectStructure = await ProjectStructure.init();
});
const projectStructure = new ProjectStructure();

const getGenerateTestDataForPageRunner = () =>
generateTestDataForPage(
Expand Down
Loading

0 comments on commit 31fa86f

Please sign in to comment.