Skip to content

Commit c59c950

Browse files
committed
refactor: functionality to extract regions from component example source files
1 parent caad0b5 commit c59c950

File tree

16 files changed

+275
-86
lines changed

16 files changed

+275
-86
lines changed

src/components-examples/BUILD.bazel

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ filegroup(
7272
)
7373

7474
highlight_files(
75-
name = "highlighted-source-files",
75+
name = "examples-highlighted",
7676
srcs = [":example-source-files"],
7777
tags = ["docs-package"],
7878
)
@@ -97,16 +97,18 @@ package_docs_content(
9797

9898
# For the live-examples in our docs, we want to package the highlighted files
9999
# into the docs content. These will be used to show the source code for examples.
100-
":highlighted-source-files": "examples-highlighted",
100+
# Note: `examples-highlighted` is a tree artifact that we want to store as is
101+
# in the docs-content. Hence there is no target section name.
102+
":examples-highlighted": "",
101103
},
102104
tags = ["docs-package"],
103105
)
104106

105107
ng_package(
106108
name = "npm_package",
107109
srcs = ["package.json"],
108-
data = [":docs-content"],
109110
entry_point = ":public-api.ts",
111+
nested_packages = [":docs-content"],
110112
tags = ["docs-package"],
111113
deps = [":components-examples"] + ALL_EXAMPLES,
112114
)

src/components-examples/material/autocomplete/autocomplete-optgroup/autocomplete-optgroup-example.html

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,14 @@
66
formControlName="stateGroup"
77
required
88
[matAutocomplete]="autoGroup">
9+
<!-- #docregion mat-autocomplete -->
910
<mat-autocomplete #autoGroup="matAutocomplete">
1011
<mat-optgroup *ngFor="let group of stateGroupOptions | async" [label]="group.letter">
1112
<mat-option *ngFor="let name of group.names" [value]="name">
1213
{{name}}
1314
</mat-option>
1415
</mat-optgroup>
1516
</mat-autocomplete>
17+
<!-- #enddocregion mat-autocomplete -->
1618
</mat-form-field>
1719
</form>
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
11
<form class="example-form">
22
<mat-form-field class="example-full-width">
3+
<!-- #docregion input -->
34
<input type="text"
45
placeholder="Pick one"
56
aria-label="Number"
67
matInput
78
[formControl]="myControl"
89
[matAutocomplete]="auto">
10+
<!-- #enddocregion input -->
11+
<!-- #docregion mat-autocomplete -->
912
<mat-autocomplete #auto="matAutocomplete">
1013
<mat-option *ngFor="let option of options" [value]="option">
1114
{{option}}
1215
</mat-option>
1316
</mat-autocomplete>
17+
<!-- #enddocregion mat-autocomplete -->
1418
</mat-form-field>
1519
</form>

tools/highlight-files/BUILD.bazel

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,16 @@ ts_library(
99
tsconfig = ":tsconfig.json",
1010
deps = [
1111
"@npm//@types/node",
12+
"@npm//@types/fs-extra",
13+
"//tools/region-parser"
1214
],
1315
)
1416

1517
nodejs_binary(
1618
name = "highlight-files",
1719
data = [
1820
":sources",
21+
"@npm//fs-extra",
1922
"@npm//highlight.js",
2023
],
2124
entry_point = ":highlight-files.ts",

tools/highlight-files/highlight-files.ts

Lines changed: 38 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@
33
* multiple input files using highlight.js. The output will be HTML files.
44
*/
55

6-
import {readFileSync, writeFileSync} from 'fs';
7-
import {extname, join} from 'path';
6+
import {readFileSync, writeFileSync, ensureDirSync} from 'fs-extra';
7+
import {dirname, extname, join, relative} from 'path';
88
import {highlightCodeBlock} from './highlight-code-block';
9+
import {regionParser} from '../region-parser/region-parser';
910

1011
/**
1112
* Determines the command line arguments for the current Bazel action. Since this action can
@@ -26,20 +27,40 @@ function getBazelActionArguments() {
2627
}
2728

2829
if (require.main === module) {
29-
// The script expects the bazel-bin path as first argument. All remaining arguments will be
30-
// considered as markdown input files that need to be transformed.
31-
const [bazelBinPath, ...inputFiles] = getBazelActionArguments();
32-
33-
// Walk through each input file and write transformed markdown output to the specified
34-
// Bazel bin directory.
35-
inputFiles.forEach(inputPath => {
36-
const fileExtension = extname(inputPath).substring(1);
37-
// Convert "my-component-example.ts" into "my-component-example-ts.html"
38-
const baseOutputPath = inputPath.replace(`.${fileExtension}`, `-${fileExtension}.html`);
39-
const outputPath = join(bazelBinPath, baseOutputPath);
40-
const htmlOutput = highlightCodeBlock(readFileSync(inputPath, 'utf8'), fileExtension);
41-
42-
writeFileSync(outputPath, htmlOutput);
43-
});
30+
// The script expects the output directory as first argument. Second is the name of the
31+
// package where this the highlight target is declared. All remaining arguments will be
32+
// considered as markdown input files that need to be transformed.
33+
34+
const [outDir, packageName, ...inputFiles] = getBazelActionArguments();
35+
36+
// Walk through each input file and write transformed markdown output
37+
// to the specified output directory.
38+
inputFiles.forEach(execPath => {
39+
// Compute a relative path from the package to the actual input file.
40+
// e.g `src/components-examples/cdk/<..>/example.ts` becomes `cdk/<..>/example.ts`.
41+
const basePath = relative(packageName, execPath);
42+
const fileExtension = extname(basePath).substring(1);
43+
const parsed = regionParser(readFileSync(execPath, 'utf8'), fileExtension);
44+
for (let region in parsed['regions']) {
45+
if (region) {
46+
const highlightedCodeSnippet = highlightCodeBlock(parsed['regions'][region],
47+
fileExtension);
48+
// Convert "my-component-example.ts" into "my-component-example_region-ts.html"
49+
const basePathOutputPath = basePath.replace(`.${fileExtension}`,
50+
`_${region}-${fileExtension}.html`);
51+
const outputPath = join(outDir, basePathOutputPath);
52+
ensureDirSync(dirname(outputPath));
53+
writeFileSync(outputPath, highlightedCodeSnippet);
54+
}
55+
}
56+
// Convert "my-component-example.ts" into "my-component-example-ts.html"
57+
const baseOutputPath = basePath.replace(`.${fileExtension}`, `-${fileExtension}.html`);
58+
const outputPath = join(outDir, baseOutputPath);
59+
const htmlOutput = highlightCodeBlock(parsed['contents'], fileExtension);
60+
61+
ensureDirSync(dirname(outputPath));
62+
writeFileSync(outputPath, htmlOutput);
63+
64+
});
4465

4566
}

tools/highlight-files/index.bzl

Lines changed: 11 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,3 @@
1-
"""
2-
Gets a path relative to the specified label. This is achieved by just removing the label
3-
package path from the specified path. e.g. the path is "guides/test/my-text.md" and the
4-
label package is "guides/". The expected path would be "test/my-text.md".
5-
"""
6-
7-
def _relative_to_label(label, short_path):
8-
# TODO(devversion): extract into generic utility under tools/
9-
return short_path[len(label.package) + 1:]
10-
111
"""
122
Implementation of the "highlight_files" rule. The implementation runs the
133
highlight-files executable in order to highlight the specified source files.
@@ -16,7 +6,7 @@ def _relative_to_label(label, short_path):
166
def _highlight_files(ctx):
177
input_files = ctx.files.srcs
188
args = ctx.actions.args()
19-
expected_outputs = []
9+
output_dir = ctx.actions.declare_directory(ctx.label.name)
2010

2111
# Do nothing if there are no input files. Bazel will throw if we schedule an action
2212
# that returns no outputs.
@@ -29,41 +19,28 @@ def _highlight_files(ctx):
2919
# Read more here: https://docs.bazel.build/versions/master/skylark/lib/Args.html#use_param_file
3020
args.use_param_file(param_file_arg = "--param-file=%s")
3121

32-
# Add the bazel bin directory to the command arguments. The script needs to know about
33-
# the output directory because the input files are not in the same location as the bazel
34-
# bin directory.
35-
args.add(ctx.bin_dir.path)
36-
37-
for input_file in input_files:
38-
# Extension of the input file (e.g. "ts" or "css")
39-
file_extension = input_file.extension
22+
# Add the output directory path to the command arguments
23+
args.add(output_dir.path)
4024

41-
# Determine the input file path relatively to the current package path. This is necessary
42-
# because we want to preserve directories for the input files and `declare_file` expects a
43-
# path that is relative to the current package. We remove the file extension including the dot
44-
# because we will constructo an output file using a different extension.
45-
relative_basepath = _relative_to_label(ctx.label, input_file.short_path)[:-len(file_extension) - 1]
25+
# Add the name of the label package. This will be used in the
26+
# action to compute package-relative paths.
27+
args.add(ctx.label.package)
4628

47-
# Construct the output path from the relative basepath and file extension. For example:
48-
# "autocomplete.ts" should result in "autocomplete-ts.html".
49-
expected_outputs += [
50-
ctx.actions.declare_file("%s-%s.html" % (relative_basepath, file_extension)),
51-
]
29+
# Add the input files to the command arguments. These files will
30+
# be processed by the highlight binary.
31+
args.add_all(input_files)
5232

53-
# Add the path for the input file to the command line arguments, so that the executable
54-
# can process it.
55-
args.add(input_file.path)
5633

5734
# Run the highlight-files executable that highlights the specified source files.
5835
ctx.actions.run(
5936
inputs = input_files,
6037
executable = ctx.executable._highlight_files,
61-
outputs = expected_outputs,
38+
outputs = [output_dir],
6239
arguments = [args],
6340
progress_message = "HighlightFiles",
6441
)
6542

66-
return DefaultInfo(files = depset(expected_outputs))
43+
return DefaultInfo(files = depset([output_dir]))
6744

6845
"""
6946
Rule definition for the "highlight_files" rule that can accept arbritary source files

tools/markdown-to-html/docs-marked-renderer.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,14 +54,14 @@ export class DocsMarkdownRenderer extends Renderer {
5454
* {
5555
* "example": "exampleName",
5656
* "file": "example-html.html",
57-
* "lines": [5, 10],
57+
* "region": "some-region",
5858
* "expanded": true
5959
* }
6060
* ) -->`
6161
* turns into
6262
* `<div material-docs-example="exampleName"
6363
* file="example-html.html"
64-
* lines="[5, 10]"
64+
* region="some-region"
6565
* expanded="true"></div>`
6666
*
6767
* (old API)
@@ -72,10 +72,10 @@ export class DocsMarkdownRenderer extends Renderer {
7272
html(html: string) {
7373
html = html.replace(exampleCommentRegex, (_match: string, content: string) => {
7474
if (content.startsWith('{')) {
75-
const {example, file, lines, expanded} = JSON.parse(content);
75+
const {example, file, region, expanded} = JSON.parse(content);
7676
return `<div material-docs-example="${example}"
7777
file="${file}"
78-
lines="${JSON.stringify(lines)}"
78+
region="${region}"
7979
expanded="${!!expanded}"></div>`;
8080
} else {
8181
return `<div material-docs-example="${content}"></div>`;

tools/package-docs-content/BUILD.bazel

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ nodejs_binary(
77
name = "package-docs-content",
88
data = [
99
":sources",
10+
"@npm//fs-extra"
1011
],
1112
entry_point = ":package-docs-content.ts",
1213
)
@@ -15,5 +16,8 @@ ts_library(
1516
name = "sources",
1617
srcs = glob(["**/*.ts"]),
1718
tsconfig = ":tsconfig.json",
18-
deps = ["@npm//@types/node"],
19+
deps = [
20+
"@npm//@types/node",
21+
"@npm//@types/fs-extra",
22+
],
1923
)

tools/package-docs-content/index.bzl

Lines changed: 25 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,23 @@
44
"""
55

66
def _package_docs_content(ctx):
7-
# Directory that will contain all grouped input files. This directory will be created
8-
# relatively to the current target package. (e.g. "bin/src/components-examples/docs-content")
9-
output_dir = ctx.attr.name
10-
117
# Arguments that will be passed to the packager executable.
128
args = ctx.actions.args()
139

14-
# List of outputs that should be generated by the packager action. Bazel will automatically
15-
# throw an error if any output has not been generated properly.
16-
expected_outputs = []
10+
# Directory that will contain all grouped input files. This directory will be
11+
# created relatively to the current target package. For example:
12+
# "bin/src/components-examples/docs-content/docs-content". The reason we need to
13+
# repeat `docs-content` is for
14+
output_dir = ctx.actions.declare_directory("%s/%s" % (ctx.attr.name, ctx.attr.name))
15+
16+
1717

1818
# Support passing arguments through a parameter file. This is necessary because on Windows
1919
# there is an argument limit and we need to handle a large amount of input files. Bazel
2020
# switches between parameter file and normal argument passing based on the operating system.
2121
# Read more here: https://docs.bazel.build/versions/master/skylark/lib/Args.html#use_param_file
22-
args.use_param_file(param_file_arg = "--param-file=%s")
22+
args.use_param_file(param_file_arg = "--param-file=%s", use_always = True)
23+
2324

2425
# Walk through each defined input target and the associated section and compute the
2526
# output file which will be added to the executable arguments.
@@ -29,25 +30,27 @@ def _package_docs_content(ctx):
2930

3031
for input_file in section_files:
3132
# Creates a relative path from the input file. We don't want to include the full
32-
# path in docs content. e.g. `docs-content/overviews/cdk/src/cdk/a11y/a11y.html`.
33-
# Instead, we want the path to be: `docs-content/overviews/cdk/a11y/a11y.html`.
34-
section_relative_file_name = input_file.short_path[len(base_dir):]
33+
# path in docs content. e.g. `/docs-content/overviews/cdk/src/cdk/a11y/a11y.html`. Instead,
34+
# we want the path to be: `/docs-content/overviews/cdk/a11y/a11y.html`.
35+
section_relative_file_name = input_file.short_path[len(base_dir) + 1:]
36+
37+
# The section name can be empty. This is reasonable when tree artifacts are copied
38+
# over to the resulting package so that only their contents are transferred.
39+
# TODO(devversion): Revisit if this can be improved so that the section name
40+
# is respected. `args.add_all` can unfold tree artifact contents.
41+
# https://cs.opensource.google/bazel/bazel/+/master:src/main/java/com/google/devtools/build/lib/analysis/skylark/Args.java;l=381-382;drc=9a6997d595fbf3447e911346034edfbde7d8b57e?q=addAll&ss=bazel
42+
if section_name:
43+
expected_out_path = "%s/%s/%s" % (output_dir.path, section_name, section_relative_file_name)
44+
else:
45+
expected_out_path = "%s/%s" % (output_dir.path, section_relative_file_name)
3546

36-
# For each input file, we want to create a copy that is stored in the output directory
37-
# within its specified section. e.g. "pkg_bin/docs-content/guides/getting-started.html"
38-
output_file = ctx.actions.declare_file(
39-
"%s/%s/%s" % (output_dir, section_name, section_relative_file_name),
40-
)
4147

42-
# Add the output file to the expected outputs so that Bazel throws an error if the file
43-
# hasn't been generated properly.
44-
expected_outputs += [output_file]
4548

4649
# Pass the input file path and the output file path to the packager executable. We need
4750
# to do this for each file because we cannot determine the general path to the output
4851
# directory in a reliable way because Bazel targets cannot just "declare" a directory.
4952
# See: https://docs.bazel.build/versions/master/skylark/lib/actions.html
50-
args.add("%s,%s" % (input_file.path, output_file.path))
53+
args.add("%s,%s" % (input_file.path, expected_out_path))
5154

5255
# Do nothing if there are no input files. Bazel will throw if we schedule an action
5356
# that returns no outputs.
@@ -59,12 +62,12 @@ def _package_docs_content(ctx):
5962
ctx.actions.run(
6063
inputs = ctx.files.srcs,
6164
executable = ctx.executable._packager,
62-
outputs = expected_outputs,
65+
outputs = [output_dir],
6366
arguments = [args],
6467
progress_message = "PackageDocsContent",
6568
)
6669

67-
return DefaultInfo(files = depset(expected_outputs))
70+
return DefaultInfo(files = depset([output_dir]))
6871

6972
"""
7073
Rule definition for the "package_docs_content" rule that can accept arbritary source files

tools/package-docs-content/package-docs-content.ts

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
* multiple times.
66
*/
77

8-
import {readFileSync, writeFileSync} from 'fs';
8+
import {readFileSync, writeFileSync, ensureDirSync, statSync, copySync} from 'fs-extra';
9+
import {dirname} from 'path';
910

1011
/**
1112
* Determines the command line arguments for the current Bazel action. Since this action can
@@ -29,10 +30,19 @@ if (require.main === module) {
2930
// Process all file pairs that have been passed to this executable. Each argument will
3031
// consist of the input file path and the desired output location.
3132
getBazelActionArguments().forEach(argument => {
32-
// Each argument that has been passed consists of an input file path and the expected
33-
// output path. e.g. {path_to_input_file},{expected_output_path}
34-
const [inputFilePath, outputPath] = argument.split(',', 2);
33+
// Each argument that has been passed consists of an input file path and the expected
34+
// output path. e.g. {path_to_input_file},{expected_output_path}
35+
const [execFilePath, expectedOutput] = argument.split(',', 2);
36+
37+
// Ensure the directory exists. Bazel does not create the tree
38+
// artifact by default.
39+
ensureDirSync(dirname(expectedOutput));
40+
41+
if (statSync(execFilePath).isDirectory()) {
42+
copySync(execFilePath, expectedOutput);
43+
} else {
44+
writeFileSync(expectedOutput, readFileSync(execFilePath, 'utf8'));
45+
}
3546

36-
writeFileSync(outputPath, readFileSync(inputFilePath, 'utf8'));
3747
});
3848
}

0 commit comments

Comments
 (0)