Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Latest commit

 

History

History
History
327 lines (307 loc) · 11.9 KB

File metadata and controls

327 lines (307 loc) · 11.9 KB
Copy raw file
Download raw file
Open symbols panel
Edit and raw actions
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
/** / \
* _ ) (( )) (
* (@) /|\ ))_(( /|\
* |-| / | \ (/\|/\) / | \ (@)
* | | ------------------------/--|-voV---\`|'/--Vov-|--\-------------------------|-|
* |-| '^` (o o) '^` | |
* | | `\Y/' |-|
* |-| Dear wary traveler, | |
* | | |-|
* |-| You have opened this file to find out why your apps build is giving you | |
* | | error messages about your new entry point not conforming to naming |-|
* |-| conventions. | |
* | | |-|
* |-| But what are the conventions you may ask? | |
* | | |-|
* |-| 1) Entry points should be in the src/sites/<site-name>/pages directory! | |
* | | 2) Entry points should only be referenced by one template file! |-|
* |-| 3) Entry points should match the name of the template they are used in! | |
* | | |-|
* |-| If you follow these rules, you will reach new levels of glorious | |
* | | enlightenment. If you do not follow these rules, dragons shall burn your |-|
* |-| village to the ground! | |
* | | |-|
* |-| You may be tempted to add your entry point to the list of silenced entry | |
* | | points, but that would be a mistake: |-|
* |-| | |
* | | a) they are only silenced because they are existing violations. |-|
* |-| b) we shouldn't be adding new violations. | |
* | | c) we'd like to fix up our old violations over time. |-|
* |-| | |
* | | With this information in hand, choose the right side of history and make |-|
* |-| your entry point conform to the naming conventions. | |
* | | |-|
* |_|____________________________________________________________________________| |
* (@) l /\ / ( ( \ /\ l `\|-|
* l / V \ \ V \ l (@)
* l/ _) )_ \I
* `\ /'
*/
const chalk = require('chalk');
const child_process = require('child_process');
const SILENCED = [
// app types loaded conditionally from _apps_dependencies.html.haml
'ailab',
'applab',
'bounce',
'calc',
'craft',
'dance',
'eval',
'fish',
'flappy',
'gamelab',
'javalab',
'jigsaw',
'maze',
'netsim',
'poetry',
'spritelab',
'studio',
'turtle',
'weblab',
// referenced from exported projects
'applab-api',
'gamelab-api',
// referenced by embedded multi/match levels
'embedBlocks',
'embedVideo',
// referenced from dashboard/public
'publicKeyCryptography',
// shared code referenced from application.html.haml. These should go
// away once we configure webpack to manage chunks dynamically.
'essential',
'code-studio',
// referenced by multiple sites
'tutorialExplorer',
'cookieBanner',
'regionalPartnerMiniContact',
// other entry points
'blockly',
'googleblockly',
'brambleHost',
'levelbuilder'
];
const SITES_CONFIG = {
studio: {
entryPrefix: '',
templateRoot: '../dashboard/app/views',
templateGlobs: ['**/*.erb', '**/*.haml'],
templateExtensions: ['erb', 'haml']
},
'code.org': {
entryPrefix: 'code.org/',
templateRoot: '../pegasus/sites.v3/code.org',
templateGlobs: ['**/*.erb', '**/*.haml'],
templateExtensions: ['erb', 'haml']
},
'hourofcode.com': {
entryPrefix: 'hourofcode.com/',
templateRoot: '../pegasus/sites.v3/hourofcode.com',
templateGlobs: ['**/*.erb', '**/*.haml'],
templateExtensions: ['erb', 'haml']
}
};
const ENTRY_POINT_FILE_PATH_PATTERN = /^\.\/src\/sites\/([\w.]+)\/pages\/(.*)\.jsx?$/;
function findTemplatesForSite(siteConfig) {
const findArgs = siteConfig.templateExtensions
.map(ext => `-name '*.${ext}'`)
.join(' -o ');
return new Promise(resolve =>
child_process.exec(
`find ${siteConfig.templateRoot} ${findArgs}`,
(err, stdout, stderr) => {
const templatesToSearch = stdout.split('\n').filter(f => f);
resolve(templatesToSearch);
}
)
);
}
function searchFilesForString(filesToSearch, searchString) {
return new Promise(resolve =>
child_process.exec(
`grep "${searchString}" "${filesToSearch.join('" "')}"`,
(err, stdout, stderr) => {
let filesWithString = stdout
.split('\n')
.filter(line => line)
.map(line => line.split(':')[0]);
resolve(filesWithString);
}
)
);
}
/**
* check a given entry point / path for compliance with the naming conventions
*
* @param entryKey string - the key for the entry point e.g. 'signup'
* @param entryPath string - the path the entry point loads e.g. 'sites/studio/pages/signup.js'
* @param stats Object - a stats object for collecting pass/fail/skip info
* @param options Object - an options config that turns on/off verbose logging
* @returns Promise<void> - a promise that resolves when it is done checking this entry point
*/
function checkEntryPoint(entryKey, entryPointPath, stats, options) {
const isEntryPointSilenced = SILENCED.includes(entryKey);
let errorColor = isEntryPointSilenced ? chalk.yellow : chalk.red;
let logVerbose = () => undefined;
const logError = console.log;
if (options.verbose) {
logVerbose = console.log;
}
const errors = [];
const entryPointPatternMatch = entryPointPath.match(
ENTRY_POINT_FILE_PATH_PATTERN
);
const site = entryPointPatternMatch && entryPointPatternMatch[1];
if (site) {
const siteConfig = SITES_CONFIG[site];
return findTemplatesForSite(siteConfig)
.then(templatesToSearch =>
searchFilesForString(templatesToSearch, `js/${entryKey}.js`)
)
.then(templates => {
// Grab the set of all valid entry keys (e.g. name of the template minus extension)
// Also convert the template paths to relative paths for easier display.
const possibleValidEntryKeys = new Set();
const matchedTemplatePaths = [];
templates.forEach(templatePath => {
const relativePath = templatePath
.replace(siteConfig.templateRoot, '')
.slice(1);
possibleValidEntryKeys.add(
siteConfig.entryPrefix + relativePath.split('.')[0]
);
matchedTemplatePaths.push(relativePath);
});
if (possibleValidEntryKeys.size === 1) {
const keyShouldBe = possibleValidEntryKeys.keys().next().value;
if (keyShouldBe !== entryKey) {
// entry point is used by only one template (good)
// but the file name of the template and the file name of the
// entry point don't match (bad)
errors.push(
`Entry point names should match the name of the file they are used in.\n` +
`This entry point should be renamed to ${chalk.underline(
keyShouldBe
)}!`
);
}
} else {
errors.push(
`Entry points should only be used by one template ` +
`but this one is used by ${possibleValidEntryKeys.size}!`
);
}
if (entryPointPatternMatch) {
// entry point is in the sites/<site-name>/pages direcotory (good)
// but it doesn't have the same name as the js file it points to (bad)
if (siteConfig.entryPrefix + entryPointPatternMatch[2] !== entryKey) {
errors.push(
`Entry points should have the same name as the file they point to!\n` +
`This entry point should be renamed to ` +
chalk.underline(
siteConfig.entryPrefix + entryPointPatternMatch[2]
) +
`!`
);
errors.push();
}
} else {
errors.push(
`Entry points should point to files in the ./src/sites/<site-name>/pages/ directory!`
);
}
// spit out the list of errors depending on
// verbosity and silencing configuration
let log = logVerbose;
if (errors.length > 0 && !isEntryPointSilenced) {
log = logError;
}
log(
errors.length === 0
? chalk.green(`✓ ${entryKey}`)
: isEntryPointSilenced
? chalk.yellow(`⚠ ${entryKey}`)
: chalk.red(`✘ ${entryKey}`),
'➜',
entryPointPath
);
if (isEntryPointSilenced) {
stats.silenced++;
if (errors.length > 0) {
log(errorColor(` ⚠ These errors have been silenced`));
}
} else if (errors.length > 0) {
stats.failed++;
} else {
stats.passed++;
}
errors.forEach(error =>
log(errorColor(` ✘ ${error.split('\n').join('\n ')}`))
);
// spit out the list of templates that refer to this entry point
if (errors.length > 0 && matchedTemplatePaths.length > 0) {
log(
chalk.yellow(
` this entry point is referenced by the following templates:`
)
);
log(
` ${siteConfig.templateRoot}/` +
chalk.blue(matchedTemplatePaths[0])
);
matchedTemplatePaths.slice(1).forEach(templatePath => {
log(
' ',
chalk.blue(
' '.repeat(siteConfig.templateRoot.length) + templatePath
)
);
});
}
});
}
// this entry point is not in a sites directory, so we don't know
// which templates to search. Just log an error and return an
// empty promise.
let log = logVerbose;
if (isEntryPointSilenced) {
stats.silenced++;
} else {
stats.failed++;
log = logError;
}
log(
isEntryPointSilenced
? chalk.yellow(`⚠ ${entryKey}`)
: chalk.red(`✘ ${entryKey}`),
'➜',
entryPointPath
);
log(errorColor(` ✘ Entry points should be in a sites directory`));
return new Promise(resolve => resolve());
}
/**
* Checks all the entry points for the given webpack config.
*
* @param webpackConfig Object - the webpack config with the entry points to check
* @param options Object - some config options
* @param options.verbose bool - whether or not to log verbosely
*
* @returns Promise<{failed: number, silenced: number, passed: number}> -
* a promise that resolves to a statsu object containing the number of
* entry points that passed/failed or were silenced.
*/
module.exports = function(webpackConfig, options = {verbose: false}) {
const stats = {
failed: 0,
silenced: 0,
passed: 0
};
const entryPromises = Object.keys(webpackConfig.entry).map(entryKey =>
checkEntryPoint(entryKey, webpackConfig.entry[entryKey][2], stats, options)
);
// The 2 index above is because the entry array has 3 elements:
// [babel-polyfill, whatwg-fetch, JS entry point]
return Promise.all(entryPromises).then(() => stats);
};
Morty Proxy This is a proxified and sanitized view of the page, visit original site.