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(plugin): create plugin to integrate modules #462

Merged
merged 41 commits into from Feb 29, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
e905aaa
feat(plugin): create plugin to integrate widgets
dsurducan Jan 10, 2024
5cbbbb3
Auto generated documentation
github-actions[bot] Jan 10, 2024
a64777c
Merge branch 'main' into widgets-build
dsurducan Jan 10, 2024
ab0d0d6
addressed pr comments
dsurducan Jan 11, 2024
c2c133b
Auto generated documentation
github-actions[bot] Jan 11, 2024
ce74a43
removed document param from widgetprops
dsurducan Jan 11, 2024
068a5ea
Auto generated documentation
github-actions[bot] Jan 11, 2024
7ecf67d
update parse function and move internal widget types file
dsurducan Jan 11, 2024
0337113
fix documentation, remove logs, remove async
dsurducan Jan 12, 2024
34134ee
Auto generated documentation
github-actions[bot] Jan 12, 2024
08b16f1
separate build per widget
dsurducan Jan 12, 2024
31d516e
Merge branch 'main' into widgets-build
dsurducan Jan 16, 2024
b9e7fbd
specify unique tailwind config for each widget
dsurducan Jan 17, 2024
dde72a0
plugin config
asanehisa Feb 14, 2024
b1c3b51
Auto generated documentation
github-actions[bot] Feb 14, 2024
24ae3ef
works w/ document
asanehisa Feb 14, 2024
967d14b
Merge branch 'main' into widgets-build
asanehisa Feb 14, 2024
7e27d86
Auto generated documentation
github-actions[bot] Feb 14, 2024
ba1374c
tests
asanehisa Feb 15, 2024
0f4c0b6
config yaml response headers work
asanehisa Feb 15, 2024
152ceb5
dont have to wrap widget code on client end
asanehisa Feb 15, 2024
5b6b3ef
wrapping works!
asanehisa Feb 15, 2024
a0b0637
add tests
asanehisa Feb 20, 2024
3336ce0
clean up a bit
asanehisa Feb 20, 2024
3a4ed01
widgets -> modules rename
asanehisa Feb 20, 2024
798d2e6
Auto generated documentation
github-actions[bot] Feb 20, 2024
951172f
confirmed works locally, address comments
asanehisa Feb 20, 2024
c0b9cac
Auto generated documentation
github-actions[bot] Feb 20, 2024
ea018a2
fix tests
asanehisa Feb 21, 2024
b18ffd6
address more comments
asanehisa Feb 21, 2024
8c756f0
add scoping plugin
asanehisa Feb 22, 2024
8ecb96a
updated plugin
asanehisa Feb 22, 2024
5f4b52d
Automated update to THIRD-PARTY-NOTICES from github action's 3rd part…
github-actions[bot] Feb 22, 2024
c87f417
Merge branch 'main' into widgets-build
asanehisa Feb 22, 2024
6673926
addressed comments
asanehisa Feb 26, 2024
7f7767f
Automated update to THIRD-PARTY-NOTICES from github action's 3rd part…
github-actions[bot] Feb 26, 2024
7385ea2
transform in rollup
asanehisa Feb 28, 2024
2eee8e8
address comments
asanehisa Feb 29, 2024
be3f42b
fix tailwind / css issues
asanehisa Feb 29, 2024
dd4e21d
remove plugin from package.json
asanehisa Feb 29, 2024
befea13
Automated update to THIRD-PARTY-NOTICES from github action's 3rd part…
github-actions[bot] Feb 29, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
34 changes: 0 additions & 34 deletions packages/pages/src/common/src/parsers/sourceFileParser.test.ts
Expand Up @@ -193,40 +193,6 @@ describe("getVariablePropertyByName", () => {
});
});

describe("addReactImports", () => {
it("correctly adds react imports", () => {
const parser = createParser(`const foo = foo;`);
parser.addReactImports();
expect(
parser.getAllText().includes(`import * as React from "react";`)
).toBeTruthy();
expect(
parser.getAllText().includes(`import * as ReactDOM from "react-dom";`)
).toBeTruthy();
});

it("doesn't add additional imports if already there", () => {
const expected =
`import * as React from "react";\n` +
`import * as ReactDOM from "react-dom";\n`;
const parser = createParser(
`import * as React from "react";\n` +
`import * as ReactDOM from "react-dom";\n`
);
parser.addReactImports();
expect(parser.getAllText()).toEqual(expected);
});

it("only adds imports if they are missing", () => {
const expected =
`import * as React from "react";\n` +
`import * as ReactDOM from "react-dom";\n`;
const parser = createParser(`import * as React from "react";`);
parser.addReactImports();
expect(parser.getAllText()).toEqual(expected);
});
});

describe("removeUnusedImports", () => {
it("correctly removes unused imports", () => {
const parser = createParser(`import * as React from "react";`);
Expand Down
27 changes: 0 additions & 27 deletions packages/pages/src/common/src/parsers/sourceFileParser.ts
Expand Up @@ -274,33 +274,6 @@ export default class SourceFileParser {
return this.sourceFile.getEnd();
}

/**
* Adds react and react-dom imports if they do not exist.
*/
addReactImports() {
const existingReactImport = this.sourceFile.getImportDeclaration(
(i) => i.getModuleSpecifierValue() === "react"
);
if (!existingReactImport) {
this.sourceFile.addImportDeclaration({
moduleSpecifier: "react",
namespaceImport: "React",
isTypeOnly: false,
});
}

const existingReactDOMImport = this.sourceFile.getImportDeclaration(
(i) => i.getModuleSpecifierValue() === "react-dom"
);
if (!existingReactDOMImport) {
this.sourceFile.addImportDeclaration({
moduleSpecifier: "react-dom",
namespaceImport: "ReactDOM",
isTypeOnly: false,
});
}
}

removeUnusedImports() {
this.sourceFile.fixUnusedIdentifiers();
}
Expand Down
95 changes: 48 additions & 47 deletions packages/pages/src/vite-plugin/modules/plugin.ts
@@ -1,4 +1,4 @@
import { build, createLogger } from "vite";
import { build, createLogger, Plugin } from "vite";
import { ProjectStructure } from "../../common/src/project/structure.js";
import { glob } from "glob";
import path from "node:path";
Expand All @@ -8,6 +8,7 @@ import { processEnvVariables } from "../../util/processEnvVariables.js";
import { nodePolyfills } from "vite-plugin-node-polyfills";
import pc from "picocolors";
import { addResponseHeadersToConfigYaml } from "../../util/editConfigYaml.js";
// https://github.com/dolanmiu/vite-plugin-scope-tailwind/issues/5
// @ts-expect-error due to type any
import scopeTailwind from "vite-plugin-scope-tailwind";
asanehisa marked this conversation as resolved.
Show resolved Hide resolved
import SourceFileParser, {
asanehisa marked this conversation as resolved.
Show resolved Hide resolved
Expand All @@ -19,19 +20,14 @@ import nested from "postcss-nested";

const wrappedCode = (moduleName: string, containerName: string): string => {
return `
asanehisa marked this conversation as resolved.
Show resolved Hide resolved
// The following code is added during build and removed after build is completed.
const moduleContainerForBuildUseOnly = document.getElementById('${containerName}');
if (!moduleContainerForBuildUseOnly) {
throw new Error('could not find ${containerName} element');
}

ReactDOM.render(
<React.StrictMode>
<${moduleName}/>
</React.StrictMode>,
moduleContainerForBuildUseOnly
);
`;
const moduleContainerForBuildUseOnly = document.getElementById('${containerName}');
if (!moduleContainerForBuildUseOnly) {
throw new Error('could not find ${containerName} element');
}
ReactDOM.render(
/* @__PURE__ */ jsx(${moduleName}, {}),
moduleContainerForBuildUseOnly
);`;
};

const moduleResponseHeaderProps = {
Expand Down Expand Up @@ -80,7 +76,6 @@ export const buildModules = async (
}

for (const [moduleName, fileInfo] of Object.entries(filepaths)) {
const index = addExtraModuleCode(fileInfo.path, moduleName);
logger.info = (msg, options) => {
if (msg.includes("building for production")) {
loggerInfo(pc.green(`\nBuilding ${moduleName} module...`));
Expand Down Expand Up @@ -117,13 +112,7 @@ export const buildModules = async (
fileInfo.name
),
},
esbuild: {
logOverride: {
"css-syntax-error": "silent",
},
asanehisa marked this conversation as resolved.
Show resolved Hide resolved
},
build: {
chunkSizeWarningLimit: 2000,
emptyOutDir: false,
asanehisa marked this conversation as resolved.
Show resolved Hide resolved
outDir: outdir,
minify: true,
Expand All @@ -138,6 +127,7 @@ export const buildModules = async (
},
define: processEnvVariables(envVarConfig.envVarPrefix),
plugins: [
addWrappedCodePlugin(fileInfo.path, moduleName),
scopeTailwind({ react: true }),
nodePolyfills({
globals: {
Expand All @@ -148,10 +138,42 @@ export const buildModules = async (
}),
],
});
removeAddedModuleCode(fileInfo.path, index);
}
};

export default function addWrappedCodePlugin(
path: string,
moduleName: string
): Plugin {
return {
name: "wrapped-code-plugin",
apply: "build",
transform(source: string, id: string) {
asanehisa marked this conversation as resolved.
Show resolved Hide resolved
if (id === path) {
return (
getReactImports(source) + source + extraModuleCode(path, moduleName)
);
}
return null;
},
};
}

const getReactImports = (source: string): string => {
let imports = "";
if (!(source.includes(`from 'react'`) || source.includes(`from "react"`))) {
imports += `import * as React from 'react';\n`;
}
if (
!(
source.includes(`from 'react-dom'`) || source.includes(`from "react-dom"`)
)
) {
imports += `import * as ReactDOM from 'react-dom';\n`;
}
return imports;
};

const shouldBundleModules = (projectStructure: ProjectStructure) => {
const { rootFolders, subfolders } = projectStructure.config;
return fs.existsSync(path.join(rootFolders.source, subfolders.modules));
Expand All @@ -168,44 +190,23 @@ const getModuleName = (modulePath: string): string | undefined => {
};

/**
* Adds custom code to module such that it works for user when bundled into umd.js.
* Adds custom code to module when bundled into umd.js.
*
* @param modulePath
* @param name set by ModuleConfig or filename
* @returns number of added index
*/
const addExtraModuleCode = (modulePath: string, name: string): number => {
const extraModuleCode = (modulePath: string, name: string) => {
const sfp = new SourceFileParser(modulePath, createTsMorphProject());
const declaration = sfp.getVariableDeclarationByType("Module");
if (declaration === undefined) {
throw new Error(`Cannot find variable Module in ${modulePath}`);
}
const moduleName = declaration.getName();
const insertIndex = sfp.getEndPos();
const afterInsertIndex = sfp.insertStatement(
wrappedCode(moduleName, name),
insertIndex
);
sfp.addReactImports();
sfp.save();
return afterInsertIndex - insertIndex;
};

/**
* Removes the custom code we added after bundling is done.
*
* @param modulePath
* @param index index added (such as via addExtraModuleCode)
*/
const removeAddedModuleCode = (modulePath: string, index: number) => {
const sfp = new SourceFileParser(modulePath, createTsMorphProject());
sfp.removeStatement(sfp.getEndPos() - index, sfp.getEndPos());
sfp.removeUnusedImports();
sfp.save();
return wrappedCode(moduleName, name);
};

/**
* Returns the postcss.config filepath if it exists in module.
* Returns the postcss.config filepath if it exists in the module.
* Else returns the root postcss.config filepath.
* If there is none, throws error.
*
Expand Down