Skip to content

Commit 02ccdb9

Browse files
committed
chore(docs.angularjs.org): serve snapshots for googlebot
This commit restores serving the plain partials (content) when a docs page is accessed with ?_escaped_fragment_=. The Google Ajax Crawler accesses these urls when the page has `<meta type="fragment" content="!">` is set. During the migration to Firebase, this was lost, which resulted in Google dropping the docs almost completely from the index. We are using a Firebase cloud function to serve the partials. Since we cannot access the static hosted files from the function, we have to deploy them as part of the function directory instead, from which they can be read. Related to angular#16432 Related to angular#16417
1 parent abe4f7d commit 02ccdb9

File tree

8 files changed

+4498
-7
lines changed

8 files changed

+4498
-7
lines changed

Gruntfile.js

+15-2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ var semver = require('semver');
1313
var exec = require('shelljs').exec;
1414
var pkg = require(__dirname + '/package.json');
1515

16+
var docsScriptFolder = 'scripts/docs.angularjs.org-firebase';
17+
1618
// Node.js version checks
1719
if (!semver.satisfies(process.version, pkg.engines.node)) {
1820
reportOrFail('Invalid node version (' + process.version + '). ' +
@@ -165,7 +167,8 @@ module.exports = function(grunt) {
165167
tmp: ['tmp'],
166168
deploy: [
167169
'deploy/docs',
168-
'deploy/code'
170+
'deploy/code',
171+
docsScriptFolder + '/functions/html'
169172
]
170173
},
171174

@@ -340,7 +343,17 @@ module.exports = function(grunt) {
340343
src: '**',
341344
dest: 'deploy/docs/',
342345
expand: true
343-
}
346+
},
347+
{
348+
src: ['build/docs/index-production.html'],
349+
dest: docsScriptFolder + '/functions/content',
350+
expand: true,
351+
flatten: true
352+
},
353+
{
354+
cwd: 'build/docs',
355+
src: 'partials/**',
356+
dest: docsScriptFolder + '/functions/content',
344357
expand: true
345358
}
346359
]

lib/grunt/utils.js

+1
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,7 @@ module.exports = {
303303
var json = grunt.file.readJSON(fileName);
304304

305305
json.hosting.public = 'deploy/docs';
306+
json.functions.source = docsScriptFolder + 'functions';
306307

307308
grunt.file.write('firebase.json', JSON.stringify(json));
308309
}

scripts/docs.angularjs.org-firebase/firebase.json

+8-3
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,14 @@
2323
"destination": "/index-production.html"
2424
},
2525
{
26-
"source": "**/*!(.jpg|.jpeg|.gif|.png|.html|.js|.map|.json|.css|.svg|.ttf|.txt|.woff|.woff2|.eot|.xml)",
27-
"destination": "/index-production.html"
26+
"source": "**/*!(.@(jpg|jpeg|gif|png|html|js|map|json|css|svg|ttf|txt|woff|woff2|eot|xml))",
27+
"function": "sendFile"
2828
}
2929
]
30+
},
31+
"functions": {
32+
"predeploy": [
33+
"npm --prefix $RESOURCE_DIR run lint"
34+
]
3035
}
31-
}
36+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
{
2+
"parserOptions": {
3+
// Required for certain syntax usages
4+
"ecmaVersion": 6
5+
},
6+
"plugins": [
7+
"promise"
8+
],
9+
"extends": "eslint:recommended",
10+
"rules": {
11+
// Removed rule "disallow the use of console" from recommended eslint rules
12+
"no-console": "off",
13+
14+
// Removed rule "disallow multiple spaces in regular expressions" from recommended eslint rules
15+
"no-regex-spaces": "off",
16+
17+
// Removed rule "disallow the use of debugger" from recommended eslint rules
18+
"no-debugger": "off",
19+
20+
// Removed rule "disallow unused variables" from recommended eslint rules
21+
"no-unused-vars": "off",
22+
23+
// Removed rule "disallow mixed spaces and tabs for indentation" from recommended eslint rules
24+
"no-mixed-spaces-and-tabs": "off",
25+
26+
// Removed rule "disallow the use of undeclared variables unless mentioned in /*global */ comments" from recommended eslint rules
27+
"no-undef": "off",
28+
29+
// Warn against template literal placeholder syntax in regular strings
30+
"no-template-curly-in-string": 1,
31+
32+
// Warn if return statements do not either always or never specify values
33+
"consistent-return": 1,
34+
35+
// Warn if no return statements in callbacks of array methods
36+
"array-callback-return": 1,
37+
38+
// Require the use of === and !==
39+
"eqeqeq": 2,
40+
41+
// Disallow the use of alert, confirm, and prompt
42+
"no-alert": 2,
43+
44+
// Disallow the use of arguments.caller or arguments.callee
45+
"no-caller": 2,
46+
47+
// Disallow null comparisons without type-checking operators
48+
"no-eq-null": 2,
49+
50+
// Disallow the use of eval()
51+
"no-eval": 2,
52+
53+
// Warn against extending native types
54+
"no-extend-native": 1,
55+
56+
// Warn against unnecessary calls to .bind()
57+
"no-extra-bind": 1,
58+
59+
// Warn against unnecessary labels
60+
"no-extra-label": 1,
61+
62+
// Disallow leading or trailing decimal points in numeric literals
63+
"no-floating-decimal": 2,
64+
65+
// Warn against shorthand type conversions
66+
"no-implicit-coercion": 1,
67+
68+
// Warn against function declarations and expressions inside loop statements
69+
"no-loop-func": 1,
70+
71+
// Disallow new operators with the Function object
72+
"no-new-func": 2,
73+
74+
// Warn against new operators with the String, Number, and Boolean objects
75+
"no-new-wrappers": 1,
76+
77+
// Disallow throwing literals as exceptions
78+
"no-throw-literal": 2,
79+
80+
// Require using Error objects as Promise rejection reasons
81+
"prefer-promise-reject-errors": 2,
82+
83+
// Enforce “for” loop update clause moving the counter in the right direction
84+
"for-direction": 2,
85+
86+
// Enforce return statements in getters
87+
"getter-return": 2,
88+
89+
// Disallow await inside of loops
90+
"no-await-in-loop": 2,
91+
92+
// Disallow comparing against -0
93+
"no-compare-neg-zero": 2,
94+
95+
// Warn against catch clause parameters from shadowing variables in the outer scope
96+
"no-catch-shadow": 1,
97+
98+
// Disallow identifiers from shadowing restricted names
99+
"no-shadow-restricted-names": 2,
100+
101+
// Enforce return statements in callbacks of array methods
102+
"callback-return": 2,
103+
104+
// Require error handling in callbacks
105+
"handle-callback-err": 2,
106+
107+
// Warn against string concatenation with __dirname and __filename
108+
"no-path-concat": 1,
109+
110+
// Prefer using arrow functions for callbacks
111+
"prefer-arrow-callback": 1,
112+
113+
// Return inside each then() to create readable and reusable Promise chains.
114+
// Forces developers to return console logs and http calls in promises.
115+
"promise/always-return": 2,
116+
117+
//Enforces the use of catch() on un-returned promises
118+
"promise/catch-or-return": 2,
119+
120+
// Warn against nested then() or catch() statements
121+
"promise/no-nesting": 1
122+
}
123+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
'use strict';
2+
3+
const functions = require('firebase-functions');
4+
const path = require('path');
5+
const fs = require('fs');
6+
7+
const BROWSER_CACHE_DURATION = 60 * 60;
8+
const CDN_CACHE_DURATION = 60 * 60 * 12;
9+
10+
const headers = {
11+
'Cache-Control': `public max-age=${BROWSER_CACHE_DURATION} s-maxage=${CDN_CACHE_DURATION}`
12+
};
13+
14+
const buildSnapshot = data => `<!DOCTYPE html>
15+
<html lang="en">
16+
<head>
17+
<meta charset="utf-8" />
18+
<meta name="viewport" content="width=device-width, initial-scale=1">
19+
<base href="/">
20+
</head>
21+
<body>
22+
${data}
23+
</body>
24+
</html>`;
25+
26+
function sendFile(request, response) {
27+
28+
const snapshotRequested = typeof request.query._escaped_fragment_ !== 'undefined';
29+
const filePath = `content/${snapshotRequested ? `partials${request.path}` : 'index-production'}.html`;
30+
31+
if (snapshotRequested) {
32+
fs.readFile(filePath, {encoding: 'utf8'}, (error, data) => {
33+
if (error) {
34+
response
35+
.status(404)
36+
.end();
37+
} else {
38+
response
39+
.set(headers)
40+
.send(buildSnapshot(data));
41+
}
42+
});
43+
} else {
44+
response
45+
.set(headers)
46+
.sendFile(filePath, {root: __dirname});
47+
}
48+
}
49+
50+
exports.sendFile = functions.https.onRequest(sendFile);

0 commit comments

Comments
 (0)