Skip to content

Commit

Permalink
Adds example of inter-mixing TypeScript and JavaScript code in the sa…
Browse files Browse the repository at this point in the history
…me bundle.

Refs #39.

This tests TypeScript depending on JavaScript for prerendering and client-side scripts. It also tests JavaScript depending on TypeScript for prerendering and client-side scripts. Most notably, for TypeScript to depend on JavaScript, the JS must define a `.d.ts` file, this is a strict Bazel requirement.
  • Loading branch information
dgp1130 committed Aug 29, 2021
1 parent 7c351f5 commit 9fd9cb0
Show file tree
Hide file tree
Showing 20 changed files with 338 additions and 0 deletions.
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"semver",
"testonly",
"transpiled",
"tsjs",
"unmocked",
"unproxied"
],
Expand Down
67 changes: 67 additions & 0 deletions examples/tsjs/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
load("@build_bazel_rules_nodejs//:index.bzl", "js_library")
load("@npm//@bazel/typescript:index.bzl", "ts_library")
load("//:index.bzl", "prerender_pages", "web_resources_devserver")
load("//tools:jasmine.bzl", "jasmine_node_test")

prerender_pages(
name = "page",
src = "page.ts",
scripts = [
":ts_parent_script",
":js_parent_script",
],
bundle_css = False, # Optimization: No CSS styling on this page.
lib_deps = ["//packages/rules_prerender"],
deps = [
"//examples/tsjs/js_parent",
"//examples/tsjs/ts_parent",
],
)

ts_library(
name = "ts_parent_script",
srcs = ["ts_parent_script.ts"],
deps = [":js_child_script"],
)

js_library(
name = "js_child_script",
srcs = [
"js_child_script.js",
"js_child_script.d.ts",
],
)

js_library(
name = "js_parent_script",
srcs = ["js_parent_script.js"],
deps = [":ts_child_script"],
)

ts_library(
name = "ts_child_script",
srcs = ["ts_child_script.ts"],
)

web_resources_devserver(
name = "devserver",
resources = ":page",
)

ts_library(
name = "test_lib",
srcs = ["test.ts"],
data = [":devserver"],
testonly = True,
deps = [
"//common:runfiles",
"//common/testing:devserver",
"//common/testing:puppeteer",
"@npm//@types/jasmine",
],
)

jasmine_node_test(
name = "test",
deps = [":test_lib"],
)
13 changes: 13 additions & 0 deletions examples/tsjs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# TS/JS

An example which verifies interoperability between TypeScript and JavaScript
`prerender_component()` targets. This includes a TypeScript component which
depends on a JavaScript component. It also includes a JavaScript component which
depends on a TypeScript component.

JavaScript depending on TypeScript works as you would expect. TypeScript
depending on JavaScript also works but has the additional requirement that the
JavaScript code must include a `.d.ts` file to provide typings for it.

JavaScript for prerendering needs to be written in CommonJS format, while
client-side JavaScript should be written ESM format.
10 changes: 10 additions & 0 deletions examples/tsjs/js_child/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
load("//:index.bzl", "prerender_component")

prerender_component(
name = "js_child",
srcs = [
"js_child.js",
"js_child.d.ts",
],
visibility = ["//examples/tsjs:__subpackages__"],
)
1 change: 1 addition & 0 deletions examples/tsjs/js_child/js_child.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export function renderJsChild(): string;
9 changes: 9 additions & 0 deletions examples/tsjs/js_child/js_child.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
function renderJsChild() {
return `
<div class="js-child">
<span>JS child</span>
</div>
`.trim();
}

module.exports = { renderJsChild };
1 change: 1 addition & 0 deletions examples/tsjs/js_child_script.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const target: string;
1 change: 1 addition & 0 deletions examples/tsjs/js_child_script.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const target = 'World';
11 changes: 11 additions & 0 deletions examples/tsjs/js_parent/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
load("//:index.bzl", "prerender_component")

prerender_component(
name = "js_parent",
srcs = [
"js_parent.js",
"js_parent.d.ts",
],
visibility = ["//examples/tsjs:__subpackages__"],
deps = ["//examples/tsjs/ts_child"],
)
1 change: 1 addition & 0 deletions examples/tsjs/js_parent/js_parent.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export function renderJsParent(): string;
12 changes: 12 additions & 0 deletions examples/tsjs/js_parent/js_parent.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
const { renderTsChild } = require('../ts_child/ts_child');

function renderJsParent() {
return `
<div class="js-parent">
<span>JS parent</span>
${renderTsChild()}
</div>
`.trim();
}

module.exports = { renderJsParent };
4 changes: 4 additions & 0 deletions examples/tsjs/js_parent_script.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { target } from './ts_child_script';

const replace = document.getElementById('replace-js-parent-script');
if (replace) replace.innerText = `Hello, ${target}!`;
92 changes: 92 additions & 0 deletions examples/tsjs/page.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { PrerenderResource, includeScript } from 'rules_prerender';
import { renderJsParent } from 'rules_prerender/examples/tsjs/js_parent/js_parent';
import { renderTsParent } from 'rules_prerender/examples/tsjs/ts_parent/ts_parent';

export default function*(): Generator<PrerenderResource, void, void> {
// Index page to list the various test cases.
yield PrerenderResource.of('/index.html', `
<!DOCTYPE html>
<html>
<head>
<title>TS/JS</title>
<meta charset="utf8">
</head>
<body>
<ul>
<li><a href="/js-depends-on-ts.html">JS depends on TS</a></li>
<li><a href="/ts-depends-on-js.html">TS depends on JS</a></li>
<li><a href="/ts-script-depends-on-js-script.html">
TS script depends on JS script
</a></li>
<li><a href="/js-script-depends-on-ts-script.html">
JS script depends on TS script
</a></li>
</ul>
</body>
</html>
`.trim());

// Test case for JS depending on TS.
yield PrerenderResource.of('/js-depends-on-ts.html', `
<!DOCTYPE html>
<html>
<head>
<title>JS depends on TS</title>
<meta charset="utf8">
</head>
<body>
${renderJsParent()}
</body>
</html>
`.trim());

// Test case for TS depending on JS.
yield PrerenderResource.of('/ts-depends-on-js.html', `
<!DOCTYPE html>
<html>
<head>
<title>TS depends on JS</title>
<meta charset="utf8">
</head>
<body>
${renderTsParent()}
</body>
</html>
`.trim());

// Test case for client-side TS depending on JS.
yield PrerenderResource.of('/ts-script-depends-on-js-script.html', `
<!DOCTYPE html>
<html>
<head>
<title>TS script depends on JS script</title>
<meta charset="utf8">
</head>
<body>
<div id="replace-ts-parent-script">
Text to be replaced by client-side JS.
<div>
${includeScript('rules_prerender/examples/tsjs/ts_parent_script')}
</body>
</html>
`.trim());

// Test case for client-side JS depending on TS.
yield PrerenderResource.of('/js-script-depends-on-ts-script.html', `
<!DOCTYPE html>
<html>
<head>
<title>JS script depends on TS script</title>
<meta charset="utf8">
</head>
<body>
<div id="replace-js-parent-script">
Text to be replaced by client-side JS.
<div>
${includeScript('rules_prerender/examples/tsjs/js_parent_script')}
</body>
</html>
`.trim());
}
78 changes: 78 additions & 0 deletions examples/tsjs/test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import 'jasmine';

import { resolveRunfile } from 'rules_prerender/common/runfiles';
import { useDevserver } from 'rules_prerender/common/testing/devserver';
import { useBrowser, usePage, puppeteerTestTimeout } from 'rules_prerender/common/testing/puppeteer';

const devserverBinary =
resolveRunfile('rules_prerender/examples/tsjs/devserver');

describe('TS/JS', () => {
const server = useDevserver(devserverBinary);
const browser = useBrowser();
const page = usePage(browser);

it('renders TypeScript depending on JavaScript', async () => {
await page.get().goto(
`http://${server.get().host}:${server.get().port}/ts-depends-on-js.html`,
{ waitUntil: 'load' },
);

const title = await page.get().title();
expect(title).toBe('TS depends on JS');

const tsParent = await page.get().$eval(
'.ts-parent > span', (el) => el.textContent);
expect(tsParent).toBe('TS parent');

const jsChild = await page.get().$eval(
'.js-child', (el) => el.textContent?.trim());
expect(jsChild).toBe('JS child');
}, puppeteerTestTimeout);

it('renders JavaScript depending on TypeScript', async () => {
await page.get().goto(
`http://${server.get().host}:${server.get().port}/js-depends-on-ts.html`,
{ waitUntil: 'load' },
);

const title = await page.get().title();
expect(title).toBe('JS depends on TS');

const jsParent = await page.get().$eval(
'.js-parent > span', (el) => el.textContent);
expect(jsParent).toBe('JS parent');

const tsChild = await page.get().$eval(
'.ts-child', (el) => el.textContent?.trim());
expect(tsChild).toBe('TS child');
}, puppeteerTestTimeout);

it('renders client-side TypeScript depending on JavaScript', async () => {
await page.get().goto(
`http://${server.get().host}:${server.get().port}/ts-script-depends-on-js-script.html`,
{ waitUntil: 'load' },
);

const title = await page.get().title();
expect(title).toBe('TS script depends on JS script');

const replaced = await page.get().$eval(
'#replace-ts-parent-script', (el) => el.textContent);
expect(replaced).toBe('Hello, World!');
}, puppeteerTestTimeout);

it('renders client-side JavaScript depending on TypeScript', async () => {
await page.get().goto(
`http://${server.get().host}:${server.get().port}/js-script-depends-on-ts-script.html`,
{ waitUntil: 'load' },
);

const title = await page.get().title();
expect(title).toBe('JS script depends on TS script');

const replaced = await page.get().$eval(
'#replace-js-parent-script', (el) => el.textContent);
expect(replaced).toBe('Hello, World!');
}, puppeteerTestTimeout);
});
7 changes: 7 additions & 0 deletions examples/tsjs/ts_child/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
load("//:index.bzl", "prerender_component")

prerender_component(
name = "ts_child",
srcs = ["ts_child.ts"],
visibility = ["//examples/tsjs:__subpackages__"],
)
7 changes: 7 additions & 0 deletions examples/tsjs/ts_child/ts_child.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export function renderTsChild(): string {
return `
<div class="ts-child">
<span>TS child</span>
</div>
`.trim();
}
1 change: 1 addition & 0 deletions examples/tsjs/ts_child_script.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const target: string = 'World';
8 changes: 8 additions & 0 deletions examples/tsjs/ts_parent/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
load("//:index.bzl", "prerender_component")

prerender_component(
name = "ts_parent",
srcs = ["ts_parent.ts"],
visibility = ["//examples/tsjs:__subpackages__"],
deps = ["//examples/tsjs/js_child"],
)
10 changes: 10 additions & 0 deletions examples/tsjs/ts_parent/ts_parent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { renderJsChild } from '../js_child/js_child';

export function renderTsParent(): string {
return `
<div class="ts-parent">
<span>TS parent</span>
${renderJsChild()}
</div>
`.trim();
}
4 changes: 4 additions & 0 deletions examples/tsjs/ts_parent_script.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { target } from './js_child_script';

const replace = document.getElementById('replace-ts-parent-script');
if (replace) replace.innerText = `Hello, ${target}!`;

0 comments on commit 9fd9cb0

Please sign in to comment.