Skip to content

Commit 20d3265

Browse files
committed
Add HtmlDependency support for service workers
1 parent 2850c36 commit 20d3265

File tree

8 files changed

+114
-3
lines changed

8 files changed

+114
-3
lines changed

news/changelog-1.2.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
## Extensions
1313

1414
- HTML dependencies may be provided by paths to files outside the extension directory
15+
- HTML dependencies may now include `serviceworkers`. `serviceworkers` will be copied into the output directory.
1516

1617
## HTML Format
1718

package/src/common/dependencies/dependencies.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ export interface PlatformDependency {
5353
function version(env: string) {
5454
const version = Deno.env.get(env);
5555
if (!version) {
56-
throw Error(`${env} isn't defined with depedency version`);
56+
throw Error(`${env} isn't defined with dependency version`);
5757
} else {
5858
return version;
5959
}

src/command/render/pandoc-dependencies-html.ts

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { pathWithForwardSlashes, safeExistsSync } from "../../core/path.ts";
1515

1616
import {
1717
DependencyFile,
18+
DependencyServiceWorker,
1819
FormatDependency,
1920
FormatExtras,
2021
kDependencies,
@@ -31,6 +32,8 @@ import {
3132
import { fixupCssReferences, isCssFile } from "../../core/css.ts";
3233

3334
import { ensureDirSync } from "fs/mod.ts";
35+
import { ProjectContext } from "../../project/types.ts";
36+
import { projectOutputDir } from "../../project/project-shared.ts";
3437

3538
export function writeDependencies(
3639
dependenciesFile: string,
@@ -54,6 +57,7 @@ export function readAndInjectDependencies(
5457
inputDir: string,
5558
libDir: string,
5659
doc: Document,
60+
project?: ProjectContext,
5761
) {
5862
const dependencyJsonStream = Deno.readTextFileSync(dependenciesFile);
5963
const htmlDependencies: FormatDependency[] = [];
@@ -77,6 +81,7 @@ export function readAndInjectDependencies(
7781
inputDir,
7882
libDir,
7983
injector,
84+
project,
8085
);
8186
injectedDependencies.push(...injected);
8287
// Finalize the injection
@@ -119,6 +124,7 @@ export function resolveDependencies(
119124
inputDir: string,
120125
libDir: string,
121126
temp: TempContext,
127+
project?: ProjectContext,
122128
) {
123129
// deep copy to not mutate caller's object
124130
extras = ld.cloneDeep(extras);
@@ -133,6 +139,7 @@ export function resolveDependencies(
133139
inputDir,
134140
libDir,
135141
injector,
142+
project,
136143
);
137144
// Finalize the injection
138145
injector.finalizeInjection();
@@ -196,6 +203,7 @@ function processHtmlDependencies(
196203
inputDir: string,
197204
libDir: string,
198205
injector: HtmlInjector,
206+
project?: ProjectContext,
199207
) {
200208
const copiedDependencies: FormatDependency[] = [];
201209
for (const dependency of dependencies) {
@@ -260,6 +268,60 @@ function processHtmlDependencies(
260268
});
261269
}
262270

271+
// Process Service Workers
272+
if (dependency.serviceworkers) {
273+
dependency.serviceworkers.forEach((serviceWorker) => {
274+
const resolveDestination = (
275+
worker: DependencyServiceWorker,
276+
inputDir: string,
277+
project?: ProjectContext,
278+
) => {
279+
// First make sure there is a destination. If omitted, provide
280+
// a default based upon the context
281+
if (!worker.destination) {
282+
if (project) {
283+
worker.destination = `/${basename(worker.source)}`;
284+
} else {
285+
worker.destination = `${basename(worker.source)}`;
286+
}
287+
}
288+
289+
// Now return either a project path or an input
290+
// relative path
291+
if (worker.destination.startsWith("/")) {
292+
if (project) {
293+
// This is a project relative path
294+
const projectDir = projectOutputDir(project);
295+
return join(projectDir, worker.destination.slice(1));
296+
} else {
297+
throw new Error(
298+
"A service worker is being provided with a project relative destination path but no valid Quarto project was found.",
299+
);
300+
}
301+
} else {
302+
// this is an input relative path
303+
return join(inputDir, worker.destination);
304+
}
305+
};
306+
307+
// Compute the path to the destination
308+
const destinationFile = resolveDestination(
309+
serviceWorker,
310+
inputDir,
311+
project,
312+
);
313+
const destinationDir = dirname(destinationFile);
314+
315+
// Ensure the directory exists and copy the source file
316+
// to the destination
317+
ensureDirSync(destinationDir);
318+
copyFileIfNewer(
319+
serviceWorker.source,
320+
destinationFile,
321+
);
322+
});
323+
}
324+
263325
// Process head HTML
264326
if (dependency.head) {
265327
injector.injectHtml(dependency.head);

src/command/render/pandoc.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1047,6 +1047,7 @@ async function resolveExtras(
10471047
inputDir,
10481048
libDir,
10491049
doc,
1050+
project,
10501051
),
10511052
);
10521053
};

src/command/render/render-files.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,7 @@ export async function renderFiles(
313313
dirname(context.target.source),
314314
context.libDir,
315315
tempContext,
316+
project,
316317
);
317318
if (extras[kIncludeInHeader]) {
318319
executeResult.includes[kIncludeInHeader] = [

src/config/types.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@ export interface FormatDependency {
206206
links?: { rel: string; href: string; type?: string }[];
207207
scripts?: DependencyHtmlFile[];
208208
stylesheets?: DependencyHtmlFile[];
209+
serviceworkers?: DependencyServiceWorker[];
209210
head?: string;
210211
resources?: DependencyFile[];
211212
}
@@ -215,6 +216,11 @@ export interface DependencyFile {
215216
path: string;
216217
}
217218

219+
export interface DependencyServiceWorker {
220+
source: string;
221+
destination?: string;
222+
}
223+
218224
export interface DependencyHtmlFile extends DependencyFile {
219225
attribs?: Record<string, string>;
220226
afterBody?: boolean;

src/execute/ojs/compile.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -797,6 +797,7 @@ export async function ojsCompile(
797797
dirname(options.source),
798798
options.libDir,
799799
options.temp,
800+
project,
800801
);
801802

802803
const ojsBundleTempFiles = [];

src/resources/pandoc/datadir/init.lua

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1485,7 +1485,7 @@ local function resolveDependencyLinkTags(linkTags)
14851485
end
14861486
end
14871487

1488-
-- Convert depedency files which may be just a string (path) or
1488+
-- Convert dependency files which may be just a string (path) or
14891489
-- incomplete objects into valid file dependencies
14901490
local function resolveFileDependencies(name, dependencyFiles)
14911491
if dependencyFiles ~= nil then
@@ -1518,8 +1518,45 @@ local function resolveFileDependencies(name, dependencyFiles)
15181518
else
15191519
return nil
15201520
end
1521+
end
1522+
1523+
-- Convert dependency files which may be just a string (path) or
1524+
-- incomplete objects into valid file dependencies
1525+
local function resolveServiceWorkers(serviceworkers)
1526+
if serviceworkers ~= nil then
1527+
-- make sure this is an array
1528+
if type(serviceworkers) ~= "table" or not utils.table.isarray(serviceworkers) then
1529+
error("Invalid HTML Dependency: serviceworkers property must be an array")
1530+
end
1531+
1532+
local finalServiceWorkers = {}
1533+
for i, v in ipairs(serviceworkers) do
1534+
if type(v) == "table" then
1535+
-- fill in the destination as the root, if one is not provided
1536+
if v.source == nil then
1537+
error("Invalid HTML Dependency: a serviceworker must have a source.")
1538+
else
1539+
v.source = resolvePathExt(v.source)
1540+
end
1541+
finalServiceWorkers[i] = v
1542+
1543+
elseif type(v) == "string" then
1544+
-- turn a string into a name and path
1545+
finalServiceWorkers[i] = {
1546+
source = resolvePathExt(v)
1547+
}
1548+
else
1549+
-- who knows what this is!
1550+
error("Invalid HTML Dependency: serviceworkers property contains an unexpected type.")
1551+
end
1552+
end
1553+
return finalServiceWorkers
1554+
else
1555+
return nil
1556+
end
15211557
end
15221558

1559+
15231560
local latexTableWithOptionsPattern = "(\\begin{table}%[%w+%])(.*)(\\end{table})"
15241561
local latexTablePattern = "(\\begin{table})(.*)(\\end{table})"
15251562
local latexLongtablePatternwWithPosAndAlign = "(\\begin{longtable}%[[^%]]+%]{.-})(.*)(\\end{longtable})"
@@ -1589,8 +1626,9 @@ quarto = {
15891626
htmlDependency.scripts == nil and
15901627
htmlDependency.stylesheets == nil and
15911628
htmlDependency.resources == nil and
1629+
htmlDependency.seviceworkers == nil and
15921630
htmlDependency.head == nil then
1593-
error("HTML dependencies must include at least one of meta, links, scripts, stylesheets, or resources. All appear empty.")
1631+
error("HTML dependencies must include at least one of meta, links, scripts, stylesheets, seviceworkers, or resources. All appear empty.")
15941632
end
15951633

15961634
-- validate that the meta is as expected
@@ -1630,6 +1668,7 @@ quarto = {
16301668
scripts = resolveDependencyFilePaths(htmlDependency.scripts),
16311669
stylesheets = resolveDependencyFilePaths(htmlDependency.stylesheets),
16321670
resources = resolveDependencyFilePaths(htmlDependency.resources),
1671+
serviceworkers = resolveServiceWorkers(htmlDependency.serviceworkers),
16331672
head = htmlDependency.head,
16341673
}))
16351674
end,

0 commit comments

Comments
 (0)