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

Import CSS and Inject into Iframe #412

Merged
merged 106 commits into from
Oct 23, 2023
Merged
Show file tree
Hide file tree
Changes from 93 commits
Commits
Show all changes
106 commits
Select commit Hold shift + click to select a range
b91d652
empty commit
alextaing Oct 9, 2023
4fb950c
empty commit
alextaing Oct 9, 2023
74c23e5
empty commit
alextaing Oct 9, 2023
daa09d1
empty commit
alextaing Oct 9, 2023
040797b
Add Component CSS imports to state (#402)
alextaing Oct 11, 2023
d4be84e
Absolute Path Page CSS Imports (#404)
alextaing Oct 12, 2023
5a8ceaa
Add `?inline` query param to user CSS imports (#409)
alextaing Oct 16, 2023
8e09d00
empty commit
alextaing Oct 9, 2023
3bf28d1
empty commit
alextaing Oct 9, 2023
0a0e11c
empty commit
alextaing Oct 9, 2023
3638056
empty commit
alextaing Oct 9, 2023
b11029d
Add Component CSS imports to state (#402)
alextaing Oct 11, 2023
eb13022
Absolute Path Page CSS Imports (#404)
alextaing Oct 12, 2023
ceb1261
Add `?inline` query param to user CSS imports (#409)
alextaing Oct 16, 2023
7270fed
empty commit
alextaing Oct 9, 2023
818d1dc
empty commit
alextaing Oct 9, 2023
f92164b
empty commit
alextaing Oct 9, 2023
c4cdf68
empty commit
alextaing Oct 9, 2023
c11a3d0
Add Component CSS imports to state (#402)
alextaing Oct 11, 2023
fc00d0f
base solution
alextaing Oct 16, 2023
676184c
ignore all screenshots
alextaing Oct 17, 2023
ba7072e
Automated linting update and features.json sync
github-actions[bot] Oct 17, 2023
5314437
extract hooks
alextaing Oct 17, 2023
cde81f0
Automated linting update and features.json sync
github-actions[bot] Oct 17, 2023
5a4701b
loops instead of stream
alextaing Oct 17, 2023
2448d82
Automated linting update and features.json sync
github-actions[bot] Oct 17, 2023
471b7a0
remove console.log
alextaing Oct 17, 2023
d472da1
Merge branch 'feature/css-in-iframe' into dev/inject-css-into-iframe
alextaing Oct 17, 2023
0f41ac8
empty commit
alextaing Oct 9, 2023
92ca699
empty commit
alextaing Oct 9, 2023
cd70dda
empty commit
alextaing Oct 9, 2023
7f7046d
empty commit
alextaing Oct 9, 2023
3841763
empty commit
alextaing Oct 9, 2023
1585fa6
empty commit
alextaing Oct 9, 2023
f135cbe
empty commit
alextaing Oct 9, 2023
c016017
empty commit
alextaing Oct 9, 2023
fde095c
Merge branch 'feature/css-in-iframe' into dev/inject-css-into-iframe
alextaing Oct 17, 2023
12d8b30
use for loops instead of streams
alextaing Oct 18, 2023
014574b
Automated linting update and features.json sync
github-actions[bot] Oct 18, 2023
ec6dde9
no duplicate style in iFrame
alextaing Oct 19, 2023
48f3287
Automated linting update and features.json sync
github-actions[bot] Oct 19, 2023
4bc6b69
also include layouts
alextaing Oct 19, 2023
4d77765
Automated linting update and features.json sync
github-actions[bot] Oct 19, 2023
4a3f178
remove active page check
alextaing Oct 19, 2023
a3318ac
Automated linting update and features.json sync
github-actions[bot] Oct 19, 2023
4f441b6
rename styletag attribute and vars
alextaing Oct 19, 2023
442f934
Automated linting update and features.json sync
github-actions[bot] Oct 19, 2023
0488322
only access active page state once
alextaing Oct 19, 2023
31e5fb0
Automated linting update and features.json sync
github-actions[bot] Oct 19, 2023
0213d4d
add warning for unfound parent styletag
alextaing Oct 19, 2023
605a4be
Automated linting update and features.json sync
github-actions[bot] Oct 19, 2023
4ef92cc
separate back
alextaing Oct 19, 2023
9c34c14
Automated linting update and features.json sync
github-actions[bot] Oct 19, 2023
fe1496b
rewording comments
alextaing Oct 19, 2023
0d40c16
Automated linting update and features.json sync
github-actions[bot] Oct 19, 2023
65ef31f
add note
alextaing Oct 19, 2023
886d8c6
Automated linting update and features.json sync
github-actions[bot] Oct 19, 2023
851634b
not subscribe to component tree
alextaing Oct 19, 2023
1fe3c2b
Automated linting update and features.json sync
github-actions[bot] Oct 19, 2023
f02247f
get absolute filepaths for page CSS
alextaing Oct 19, 2023
aaea13a
Automated linting update and features.json sync
github-actions[bot] Oct 19, 2023
6b1a295
cannot test using css file that does not exist
alextaing Oct 20, 2023
434d6d7
Automated linting update and features.json sync
github-actions[bot] Oct 20, 2023
8f09de5
fix css file writing
alextaing Oct 20, 2023
1bf9864
Automated linting update and features.json sync
github-actions[bot] Oct 20, 2023
43fcd81
unix conversion for windows
alextaing Oct 20, 2023
31b0539
remove console.log
alextaing Oct 20, 2023
de2a3fa
windows path to unix for components
alextaing Oct 20, 2023
828c84c
Automated linting update and features.json sync
github-actions[bot] Oct 20, 2023
9666fe9
import for lodash isequal
alextaing Oct 20, 2023
d1eeec0
fix for windows
alextaing Oct 20, 2023
a1ecec9
Automated linting update and features.json sync
github-actions[bot] Oct 20, 2023
c468ac0
better names
alextaing Oct 20, 2023
b217fe4
Automated linting update and features.json sync
github-actions[bot] Oct 20, 2023
bee3127
better name
alextaing Oct 20, 2023
ab7f9d3
more docs
alextaing Oct 20, 2023
91dda0c
Automated linting update and features.json sync
github-actions[bot] Oct 20, 2023
7ce0310
better names
alextaing Oct 20, 2023
4e0b709
rename to trees, extract function, docs
alextaing Oct 20, 2023
5e8317e
Automated linting update and features.json sync
github-actions[bot] Oct 20, 2023
03bf8ee
componentTreeCss
alextaing Oct 20, 2023
a6e28fc
use spread
alextaing Oct 20, 2023
f1ca308
rename func
alextaing Oct 20, 2023
3ff3121
Automated linting update and features.json sync
github-actions[bot] Oct 20, 2023
0b8ebdf
add space
alextaing Oct 20, 2023
4e5d0b9
ends with css
alextaing Oct 20, 2023
2864bae
readd tests for node module CSS
alextaing Oct 23, 2023
1c56d0e
Automated linting update and features.json sync
github-actions[bot] Oct 23, 2023
4486774
return once css array
alextaing Oct 23, 2023
d09e10e
Automated linting update and features.json sync
github-actions[bot] Oct 23, 2023
b9dafc7
better docs
alextaing Oct 23, 2023
f9363da
Automated linting update and features.json sync
github-actions[bot] Oct 23, 2023
bb1e0f9
Merge branch 'feature/css-in-iframe' into dev/inject-css-into-iframe
alextaing Oct 23, 2023
a6db12e
comments round 1
alextaing Oct 23, 2023
3e29311
Automated linting update and features.json sync
github-actions[bot] Oct 23, 2023
903ae9b
unsupported
alextaing Oct 23, 2023
7498e86
delete file
alextaing Oct 23, 2023
d755105
fix tests
alextaing Oct 23, 2023
d293f40
all caps, move existingIframeStyletag check
alextaing Oct 23, 2023
c3ca368
Automated linting update and features.json sync
github-actions[bot] Oct 23, 2023
04bf4dd
simplify loadStyling loops
alextaing Oct 23, 2023
1f4fc4c
Automated linting update and features.json sync
github-actions[bot] Oct 23, 2023
c197638
use stream func and add return tupe
alextaing Oct 23, 2023
9bb8314
use type predicate
alextaing Oct 23, 2023
6b516d9
flatmap
alextaing Oct 23, 2023
d1baf1e
Automated linting update and features.json sync
github-actions[bot] Oct 23, 2023
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
3 changes: 1 addition & 2 deletions e2e-tests/playwright.config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { PlaywrightTestConfig, expect } from "@playwright/test";
import fs from "node:fs";
import os from "node:os";

expect.extend({
async toHaveContents(filepath: string, expectedContents: string) {
Expand Down Expand Up @@ -52,7 +51,7 @@ const config: PlaywrightTestConfig = {
video: "on",
},
workers: 1,
ignoreSnapshots: os.platform() !== "darwin",
ignoreSnapshots: true,
};

export default config;
1 change: 1 addition & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/studio-plugin/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
"colors": "^1.4.0",
"cross-fetch": "^4.0.0",
"dependency-tree": "^10.0.9",
"filing-cabinet": "^4.1.6",
"kill-port": "^2.0.1",
"prettier": "2.8.3",
"simple-git": "^3.16.0",
Expand Down
43 changes: 36 additions & 7 deletions packages/studio-plugin/src/orchestrators/ParsingOrchestrator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,16 @@ export function createTsMorphProject(tsConfigFilePath: string) {
*/
export default class ParsingOrchestrator {
private filepathToFileMetadata: Record<string, FileMetadata> = {};
private filepathToDependencyTree: Record<string, Tree> = {};
private pageNameToPageFile: Record<string, PageFile> = {};
private siteSettingsFile?: SiteSettingsFile;
private studioData?: StudioData;
private paths: UserPaths;
private layoutOrchestrator: LayoutOrchestrator;
/**
* Each key in this object is a ComponentFile filepath which
* maps to the rest of the ComponentFile's dependency tree.
*/
private dependencyTreesObject: Record<string, Tree> = {};

/** All paths are assumed to be absolute. */
constructor(
Expand Down Expand Up @@ -113,7 +117,10 @@ export default class ParsingOrchestrator {
}

if (filepath.startsWith(this.paths.components)) {
delete this.filepathToDependencyTree[filepath];
const componentDepTreeRoot = this.getComponentDepTreeRoot(filepath);
if (componentDepTreeRoot) {
delete this.dependencyTreesObject[componentDepTreeRoot];
}
if (this.filepathToFileMetadata.hasOwnProperty(filepath)) {
const originalMetadataUUID =
this.filepathToFileMetadata[filepath].metadataUUID;
Expand Down Expand Up @@ -197,11 +204,15 @@ export default class ParsingOrchestrator {
});

if (absPath.startsWith(this.paths.components)) {
this.updateFilepathToDependencyTree(absPath);
this.updateDependencyTrees(absPath);
const componentDepTreeRoot = this.getComponentDepTreeRoot(absPath);
if (!componentDepTreeRoot) {
throw new Error(`Could not find dependency tree for ${absPath}`);
}
const componentFile = new ComponentFile(
absPath,
this.project,
this.filepathToDependencyTree[absPath]
this.dependencyTreesObject[componentDepTreeRoot]
);
const result = componentFile.getComponentMetadata();
if (result.isErr) {
Expand All @@ -216,12 +227,30 @@ export default class ParsingOrchestrator {
);
};

private updateFilepathToDependencyTree(absPath: string) {
this.filepathToDependencyTree[absPath] = dependencyTree({
private updateDependencyTrees(absPath: string) {
const newDepTree = dependencyTree({
filename: absPath,
directory: upath.dirname(absPath),
visited: this.filepathToDependencyTree,
visited: this.dependencyTreesObject,
});
if (typeof newDepTree === "string") {
throw new Error(`Invalid dependency tree returned for ${absPath}.`);
}
this.dependencyTreesObject = {
...this.dependencyTreesObject,
...newDepTree,
};
}

/**
* Given a Unix filepath, this function finds the corresponding
* dependency tree root. This is important since the paths within
* the dependency tree may be either Unix or Windows.
*/
private getComponentDepTreeRoot(unixFilepath: string) {
return Object.keys(this.dependencyTreesObject).find(
(path) => upath.toUnix(path) === unixFilepath
);
}

private initPageNameToPageFile(): Record<string, PageFile> {
Expand Down
29 changes: 20 additions & 9 deletions packages/studio-plugin/src/parsers/StudioSourceFileParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import TypeNodeParsingHelper, {
import { parseSync as babelParseSync } from "@babel/core";
import NpmLookup from "./helpers/NpmLookup";
import { TypelessPropVal } from "../types";
import cabinet from "filing-cabinet";

export type ParsedImport = {
importSource: string;
Expand Down Expand Up @@ -123,19 +124,29 @@ export default class StudioSourceFileParser {
parseCssImports(): string[] {
const cssImports: string[] = [];

const resolveRelativeFilepath = (filepath: string) => {
if (filepath.startsWith(".")) {
return upath.resolve(
upath.dirname(this.sourceFile.getFilePath()),
filepath
const getAbsoluteImportFilepath = (importPath: string) => {
if (upath.isAbsolute(importPath)) {
return upath.toUnix(importPath);
}
const resolvedPath = cabinet({
partial: importPath,
directory: upath.dirname(this.sourceFile.getFilePath()),
filename: this.sourceFile.getFilePath(),
});
if (!resolvedPath) {
throw new Error(
`${importPath} could not be resolved when parsing ` +
`${this.sourceFile.getFilePath()} for CSS imports.`
);
}
return filepath;
return upath.toUnix(resolvedPath);
};

this.sourceFile.getImportDeclarations().forEach((importDeclaration) => {
const { source } = StaticParsingHelpers.parseImport(importDeclaration);
if (source.endsWith(".css")) {
cssImports.push(resolveRelativeFilepath(source));
const { source: importPath } =
StaticParsingHelpers.parseImport(importDeclaration);
if (importPath.endsWith(".css")) {
cssImports.push(getAbsoluteImportFilepath(importPath));
}
});
return cssImports;
Expand Down
6 changes: 4 additions & 2 deletions packages/studio-plugin/src/sourcefiles/ComponentFile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import tryUsingResult from "../errors/tryUsingResult";
import { ParsingError, ParsingErrorKind } from "../errors/ParsingError";
import { Result } from "true-myth";
import { Tree } from "dependency-tree";
import upath from "upath";

/**
* ComponentFile is responsible for parsing a single component file, for example
Expand Down Expand Up @@ -62,8 +63,9 @@ export default class ComponentFile {
function getCssFilesFromDependencyTree(dependencyTree: Tree): string[] {
const cssFiles = Object.entries(dependencyTree).reduce(
(cssFiles, [filename, subDependencyTree]) => {
if (filename.includes(".css")) {
cssFiles.add(filename);
const unixFilepath = upath.toUnix(filename);
nmanu1 marked this conversation as resolved.
Show resolved Hide resolved
nmanu1 marked this conversation as resolved.
Show resolved Hide resolved
if (unixFilepath.endsWith(".css")) {
cssFiles.add(unixFilepath);
}
return new Set([
...cssFiles,
Expand Down
9 changes: 4 additions & 5 deletions packages/studio-plugin/src/writers/StudioSourceFileWriter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import prettier from "prettier";
import fs from "fs";
import { PropVal, PropValueKind, PropValues, PropValueType } from "../types";
import { getImportSpecifierWithExtension } from "../utils/getImportSpecifier";
import upath from "upath";

/**
* StudioSourceFileWriter contains shared business logic for
Expand Down Expand Up @@ -79,12 +78,12 @@ export default class StudioSourceFileWriter {
});
this.sourceFile.fixMissingImports();
cssImports?.forEach((importSource) => {
const moduleSpecifier = upath.isAbsolute(importSource)
? getImportSpecifierWithExtension(
const moduleSpecifier = importSource.includes("/node_modules/")
? importSource.split("/node_modules/")[1]
nmanu1 marked this conversation as resolved.
Show resolved Hide resolved
: getImportSpecifierWithExtension(
this.sourceFile.getFilePath(),
importSource
)
: importSource;
);
this.sourceFile.addImportDeclaration({ moduleSpecifier });
});
this.sourceFile.organizeImports();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import "./index.css";
import "@yext/search-ui-react/lib/bundle.css";

import ComplexBanner from "../ComponentFile/ComplexBanner";
import "@yext/search-ui-react/index.css";
alextaing marked this conversation as resolved.
Show resolved Hide resolved
import { TemplateProps } from "@yext/pages";

export default function BasicLayout({ document }: TemplateProps) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
div {
background-color: lightsalmon;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import "@yext/search-ui-react/bundle.css";
import ComplexBanner from "../ComponentFile/ComplexBanner";
import { GetPath, TemplateConfig, TemplateProps } from "@yext/pages";

export const config: TemplateConfig = {
stream: {
$id: "studio-stream-id",
localization: { locales: ["en"] },
filter: { entityTypes: ["location"] },
fields: ["title", "slug"],
},
};

export const getPath: GetPath<TemplateProps> = ({
document,
}: TemplateProps) => {
return document.slug;
};

export default function BrokenCss({ document }: TemplateProps) {
nmanu1 marked this conversation as resolved.
Show resolved Hide resolved
return <ComplexBanner title={document.title} num={3} bool={false} />;
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import "./index.css";
import "@yext/search-ui-react/lib/bundle.css";

import ComplexBanner from "../ComponentFile/ComplexBanner";
import "@yext/search-ui-react/index.css";
import { GetPath, TemplateConfig, TemplateProps } from "@yext/pages";

export const config: TemplateConfig = {
Expand Down
3 changes: 3 additions & 0 deletions packages/studio-plugin/tests/__fixtures__/PageFile/index.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
div {
background-color: lightsalmon;
}
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ describe("getComponentMetadata", () => {

it("correctly parses a string literal type", () => {
const pathToComponent = getComponentPath("StringLiteralBanner");
const componentFile = new ComponentFile(pathToComponent, project);
const componentFile = new ComponentFile(pathToComponent, project, {});
nmanu1 marked this conversation as resolved.
Show resolved Hide resolved
const result = componentFile.getComponentMetadata();
assertIsOk(result);
expect(result.value.propShape).toEqual({
Expand All @@ -191,7 +191,7 @@ describe("getComponentMetadata", () => {

it("can parse a type that shares the same name as a Studio type", () => {
const pathToComponent = getComponentPath("StudioTypeNameBanner");
const componentFile = new ComponentFile(pathToComponent, project);
const componentFile = new ComponentFile(pathToComponent, project, {});
const result = componentFile.getComponentMetadata();
assertIsOk(result);
expect(result.value.propShape).toEqual({
Expand All @@ -212,15 +212,15 @@ describe("getComponentMetadata", () => {

it("Throws an Error if HexColor is not imported from Studio", () => {
const pathToComponent = getComponentPath("NonStudioImportBanner");
const componentFile = new ComponentFile(pathToComponent, project);
const componentFile = new ComponentFile(pathToComponent, project, {});
expect(componentFile.getComponentMetadata()).toHaveErrorMessage(
"Prop type HexColor is invalid because it is not imported from @yext/studio"
);
});

it("Throws an Error if imported prop interface is missing HexColor import", () => {
const pathToComponent = getComponentPath("MissingExternalImportBanner");
const componentFile = new ComponentFile(pathToComponent, project);
const componentFile = new ComponentFile(pathToComponent, project, {});
expect(componentFile.getComponentMetadata()).toHaveErrorMessage(
"Prop type HexColor is invalid because it is not imported from @yext/studio"
);
Expand Down
4 changes: 3 additions & 1 deletion packages/studio-plugin/tests/sourcefiles/LayoutFile.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,9 @@ describe("getLayoutState", () => {
const expectedIndexCssPath = getFixturePath("LayoutFile/index.css");
expect(result.value.cssImports).toEqual([
expectedIndexCssPath,
"@yext/search-ui-react/index.css",
expect.stringContaining(
"/node_modules/@yext/search-ui-react/lib/bundle.css"
),
]);
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,9 @@ describe("getPageState", () => {
const expectedIndexCssPath = getFixturePath("PageFile/index.css");
expect(result.value.cssImports).toEqual([
expectedIndexCssPath,
"@yext/search-ui-react/index.css",
expect.stringContaining(
"/node_modules/@yext/search-ui-react/lib/bundle.css"
),
]);
});

Expand Down Expand Up @@ -124,5 +126,13 @@ describe("getPageState", () => {
);
expect(consoleErrorSpy).toHaveBeenCalledTimes(2);
});

it("cannot resolve node_module CSS import using package.json export alias", () => {
const pageFile = createPageFile("brokenCssImport");

expect(pageFile.getPageState()).toHaveErrorMessage(
/^@yext\/search-ui-react\/bundle.css could not be resolved /
);
});
});
});
3 changes: 2 additions & 1 deletion packages/studio-ui/src/AppWithLazyLoading.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import LoadingOverlay from "./components/LoadingOverlay";
import { Suspense, lazy, useEffect, useState } from "react";
import useStudioStore from "./store/useStudioStore";
import ProgressBar from "./components/ProgressBar";
import loadComponents from "./utils/loadComponents";
import { loadComponents, loadStyling } from "./utils/loadUserAssets";
import classNames from "classnames";

const AppPromise = import("./App");
Expand All @@ -18,6 +18,7 @@ export default function AppWithLazyLoading() {

useEffect(() => {
loadComponents();
nmanu1 marked this conversation as resolved.
Show resolved Hide resolved
void loadStyling();
alextaing marked this conversation as resolved.
Show resolved Hide resolved
void AppPromise.then(() => setAppLoaded(true));
}, []);

Expand Down
Loading
Loading