@@ -75,49 +75,13 @@ shiny_prerendered_html <- function(input_rmd, encoding, render_args) {
75
75
76
76
# determine whether we need to render the Rmd in advance
77
77
prerender_option <- tolower(Sys.getenv(" RMARKDOWN_RUN_PRERENDER" , " 1" ))
78
-
79
- if (file.access(output_dir , 2 ) != 0 ) {
80
- if (! file.exists(rendered_html ))
81
- stop(" Unable to write prerendered HTML file to " , rendered_html )
82
-
83
- prerender <- FALSE
84
- }
85
- else if (identical(prerender_option , " 0" )) {
86
- prerender <- FALSE
87
- }
88
- else if (identical(prerender_option , " 1" )) {
89
-
90
- # determine the last modified time of the output file
91
- if (file.exists(rendered_html ))
92
- output_last_modified <- as.integer(file.info(rendered_html )$ mtime )
93
- else
94
- output_last_modified <- 0L
95
-
96
- # short circuit for Rmd modified. if it hasn't been modified since the
97
- # html was generated look at external resources
98
- input_last_modified <- as.integer(file.info(input_rmd )$ mtime )
99
- if (input_last_modified > output_last_modified ) {
100
- prerender <- TRUE
101
- }
102
- else {
103
- # find external resources referenced by the file
104
- external_resources <- find_external_resources(input_rmd , encoding )
105
-
106
- # get paths to external resources
107
- input_files <- c(input_rmd ,
108
- file.path(output_dir , external_resources $ path ))
109
-
110
- # what's the maximum last_modified time of an input file
111
- input_last_modified <- max(as.integer(file.info(input_files )$ mtime ),
112
- na.rm = TRUE )
113
-
114
- # render if an input file was modified after the output file
115
- prerender <- input_last_modified > output_last_modified
116
- }
117
- }
118
- else {
119
- stop(" Invalid value '" , prerender_option , " ' for RMARKDOWN_RUN_PRERENDER" )
120
- }
78
+ prerender <- shiny_prerendered_prerender(
79
+ input_rmd ,
80
+ rendered_html ,
81
+ output_dir ,
82
+ encoding ,
83
+ prerender_option
84
+ )
121
85
122
86
# prerender if necessary
123
87
if (prerender ) {
@@ -171,17 +135,114 @@ shiny_prerendered_html <- function(input_rmd, encoding, render_args) {
171
135
shinyHTML_with_deps(rendered_html , dependencies )
172
136
}
173
137
138
+ shiny_prerendered_prerender <- function (
139
+ input_rmd ,
140
+ rendered_html ,
141
+ output_dir ,
142
+ encoding ,
143
+ prerender_option
144
+ ) {
145
+ if (file.access(output_dir , 2 ) != 0 ) {
146
+ if (! file.exists(rendered_html ))
147
+ stop(" Unable to write prerendered HTML file to " , rendered_html )
148
+ return (FALSE )
149
+ }
150
+
151
+ if (identical(prerender_option , " 0" )) {
152
+ return (FALSE )
153
+ }
154
+ if (! identical(prerender_option , " 1" )) {
155
+ stop(" Invalid value '" , prerender_option , " ' for RMARKDOWN_RUN_PRERENDER" )
156
+ }
157
+
158
+ # determine the last modified time of the output file
159
+ if (file.exists(rendered_html )) {
160
+ output_last_modified <- as.integer(file.info(rendered_html )$ mtime )
161
+ } else {
162
+ output_last_modified <- 0L
163
+ }
164
+
165
+ # short circuit for Rmd modified. if it hasn't been modified since the
166
+ # html was generated look at external resources
167
+ input_last_modified <- as.integer(file.info(input_rmd )$ mtime )
168
+ if (input_last_modified > output_last_modified ) {
169
+ return (TRUE )
170
+ }
171
+
172
+ # find external resources referenced by the file
173
+ external_resources <- find_external_resources(input_rmd , encoding )
174
+
175
+ # get paths to external resources
176
+ input_files <- c(input_rmd , file.path(output_dir , external_resources $ path ))
177
+
178
+ # what's the maximum last_modified time of an input file
179
+ input_last_modified <- max(as.integer(file.info(input_files )$ mtime ), na.rm = TRUE )
180
+
181
+ # render if an input file was modified after the output file
182
+ if (input_last_modified > output_last_modified ) {
183
+ return (TRUE )
184
+ }
185
+
186
+ html_lines <- readLines(rendered_html , encoding = " UTF-8" , warn = FALSE )
187
+
188
+ # check that all html dependencies exist
189
+ dependencies_json <- shiny_prerendered_extract_context(html_lines , " dependencies" )
190
+ dependencies <- jsonlite :: unserializeJSON(dependencies_json )
191
+
192
+ pkgsSeen <- list ()
193
+ for (dep in dependencies ) {
194
+ if (is.null(dep $ package )) {
195
+ # if the file doesn't exist at all, render again
196
+ if (! file.exists(dep $ src $ file )) {
197
+ # might create a missing file compile-time error,
198
+ # but that's better than a missing file prerendered error
199
+ return (TRUE )
200
+ }
201
+ } else {
202
+ depPkg <- dep $ package
203
+ depVer <- dep $ pkgVersion
204
+ if (is.null(pkgsSeen [[depPkg ]])) {
205
+ # has not seen pkg
206
+
207
+ # depVer could be NULL, producing a logical(0)
208
+ # means old prerender version, render again
209
+ if (! isTRUE(get_package_version_string(depPkg ) == depVer )) {
210
+ # was not rendered with the same R package. must render again
211
+ return (TRUE )
212
+ }
213
+ pkgsSeen [[depPkg ]] <- depVer
214
+ }
215
+ }
216
+ }
217
+ # all html dependencies are accounted for
218
+
219
+ # check for execution package version differences
220
+ execution_json <- shiny_prerendered_extract_context(html_lines , " execution_dependencies" )
221
+ execution_info <- jsonlite :: unserializeJSON(execution_json )
222
+ execution_pkg_names <- execution_info $ packages $ package
223
+ execution_pkg_versions <- execution_info $ packages $ version
224
+ for (i in seq_along(execution_pkg_names )) {
225
+ if (! identical(
226
+ get_package_version_string(execution_pkg_names [i ]),
227
+ execution_pkg_versions [i ]
228
+ )) {
229
+ return (TRUE )
230
+ }
231
+ }
232
+ # all execution packages match
233
+
234
+ return (FALSE )
235
+ }
236
+
174
237
175
238
# Write the dependencies for a shiny_prerendered document.
176
239
shiny_prerendered_append_dependencies <- function (input , # always UTF-8
177
240
shiny_prerendered_dependencies ,
178
241
files_dir ,
179
242
output_dir ) {
180
243
181
-
182
-
183
244
# transform dependencies (if we aren't in debug mode)
184
- dependencies <- lapply(shiny_prerendered_dependencies , function (dependency ) {
245
+ dependencies <- lapply(shiny_prerendered_dependencies $ deps , function (dependency ) {
185
246
186
247
# no transformation in dev mode (so browser dev tools can map directly
187
248
# to the locations of CSS and JS files in their pkg src directory)
@@ -199,6 +260,8 @@ shiny_prerendered_append_dependencies <- function(input, # always UTF-8
199
260
package_desc <- read.dcf(file.path(package_dir , " DESCRIPTION" ),
200
261
all = TRUE )
201
262
dependency $ package <- package_desc $ Package
263
+ # named to something that doesn't start with 'package' to deter lazy name matching
264
+ dependency $ pkgVersion <- package_desc $ Version
202
265
dependency $ src $ file <- normalized_relative_to(package_dir ,
203
266
dependency $ src $ file )
204
267
}
@@ -225,6 +288,14 @@ shiny_prerendered_append_dependencies <- function(input, # always UTF-8
225
288
# write deps to connection
226
289
dependencies_json <- jsonlite :: serializeJSON(dependencies , pretty = FALSE )
227
290
shiny_prerendered_append_context(con , " dependencies" , dependencies_json )
291
+
292
+ # write r major version and execution package dependencies
293
+ execution_json <- jsonlite :: serializeJSON(
294
+ # visibly display what is being stored
295
+ shiny_prerendered_dependencies [" packages" ],
296
+ pretty = FALSE
297
+ )
298
+ shiny_prerendered_append_context(con , " execution_dependencies" , execution_json )
228
299
}
229
300
230
301
@@ -324,7 +395,7 @@ shiny_prerendered_option_hook <- function(input, encoding) {
324
395
options $ cache > 0 )
325
396
data_file <- to_utf8(data_file , encoding )
326
397
data_dir <- shiny_prerendered_data_dir(input , create = TRUE )
327
- index_file <- shiny_prerendred_data_chunks_index (data_dir )
398
+ index_file <- shiny_prerendered_data_chunks_index (data_dir )
328
399
conn <- file(index_file , open = " ab" , encoding = " UTF-8" )
329
400
on.exit(close(conn ), add = TRUE )
330
401
write(data_file , file = conn , append = TRUE )
@@ -406,7 +477,7 @@ shiny_prerendered_evaluate_hook <- function(input) {
406
477
shiny_prerendered_remove_uncached_data <- function (input ) {
407
478
data_dir <- shiny_prerendered_data_dir(input )
408
479
if (dir_exists(data_dir )) {
409
- index_file <- shiny_prerendred_data_chunks_index (data_dir )
480
+ index_file <- shiny_prerendered_data_chunks_index (data_dir )
410
481
if (file.exists(index_file ))
411
482
unlink(index_file )
412
483
rdata_files <- list.files(data_dir , pattern = utils :: glob2rx(" *.RData" ))
@@ -562,7 +633,7 @@ shiny_prerendered_data_load <- function(input_rmd, server_envir) {
562
633
data_dir <- shiny_prerendered_data_dir(input_rmd )
563
634
if (dir_exists(data_dir )) {
564
635
# read index of data files
565
- index_file <- shiny_prerendred_data_chunks_index (data_dir )
636
+ index_file <- shiny_prerendered_data_chunks_index (data_dir )
566
637
if (file.exists(index_file )) {
567
638
rdata_files <- readLines(index_file , encoding = " UTF-8" )
568
639
# load each of the files in the index
@@ -576,7 +647,7 @@ shiny_prerendered_data_load <- function(input_rmd, server_envir) {
576
647
}
577
648
578
649
# File used to store names of chunks which had cache=TRUE during the last render
579
- shiny_prerendred_data_chunks_index <- function (data_dir ) {
650
+ shiny_prerendered_data_chunks_index <- function (data_dir ) {
580
651
file.path(data_dir , " data_chunks_index.txt" )
581
652
}
582
653
@@ -585,4 +656,3 @@ shiny_prerendered_data_file_name <- function(label, cache) {
585
656
type <- ifelse(cache , " .cached" , " " )
586
657
sprintf(" %s%s.RData" , label , type )
587
658
}
588
-
0 commit comments