linguist-js 2.4.2 → 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -19,6 +19,7 @@ commander_1.program
19
19
  .option('-j|--json [bool]', 'Display the output as JSON', false)
20
20
  .option('-t|--tree <traversal>', 'Which part of the output JSON to display (dot-delimited)')
21
21
  .option('-q|--quick [bool]', 'Skip complex language analysis (alias for -{A|I|H|S}=false)', false)
22
+ .option('-o|--offline [bool]', 'Use packaged data files instead of fetching latest from GitHub', false)
22
23
  .option('-V|--keepVendored [bool]', 'Prevent skipping over vendored/generated files', false)
23
24
  .option('-B|--keepBinary [bool]', 'Prevent skipping over binary files', false)
24
25
  .option('-r|--relativePaths [bool]', 'Convert absolute file paths to relative', false)
@@ -3,11 +3,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ const promises_1 = __importDefault(require("fs/promises"));
7
+ const path_1 = __importDefault(require("path"));
6
8
  const cross_fetch_1 = __importDefault(require("cross-fetch"));
7
9
  const node_cache_1 = __importDefault(require("node-cache"));
8
10
  const cache = new node_cache_1.default({});
9
- /** Load a data file from github-linguist. */
10
- async function loadFile(file) {
11
+ async function loadWebFile(file) {
11
12
  // Return cache if it exists
12
13
  const cachedContent = cache.get(file);
13
14
  if (cachedContent)
@@ -18,4 +19,12 @@ async function loadFile(file) {
18
19
  cache.set(file, fileContent);
19
20
  return fileContent;
20
21
  }
22
+ async function loadLocalFile(file) {
23
+ const filePath = path_1.default.resolve(__dirname, '../../ext', file);
24
+ return promises_1.default.readFile(filePath).then(buffer => buffer.toString());
25
+ }
26
+ /** Load a data file from github-linguist. */
27
+ async function loadFile(file, offline = false) {
28
+ return offline ? loadLocalFile(file) : loadWebFile(file);
29
+ }
21
30
  exports.default = loadFile;
package/dist/index.js CHANGED
@@ -20,11 +20,11 @@ async function analyse(input, opts = {}) {
20
20
  input = [input !== null && input !== void 0 ? input : []].flat();
21
21
  opts.fileContent = [(_a = opts.fileContent) !== null && _a !== void 0 ? _a : []].flat();
22
22
  // Load data from github-linguist web repo
23
- const langData = await (0, load_data_1.default)('languages.yml').then(js_yaml_1.default.load);
24
- const vendorData = await (0, load_data_1.default)('vendor.yml').then(js_yaml_1.default.load);
25
- const docData = await (0, load_data_1.default)('documentation.yml').then(js_yaml_1.default.load);
26
- const heuristicsData = await (0, load_data_1.default)('heuristics.yml').then(js_yaml_1.default.load);
27
- const generatedData = await (0, load_data_1.default)('generated.rb').then(text => { var _a; return (_a = text.match(/(?<=name\.match\(\/).+?(?=(?<!\\)\/)/gm)) !== null && _a !== void 0 ? _a : []; });
23
+ const langData = await (0, load_data_1.default)('languages.yml', opts.offline).then(js_yaml_1.default.load);
24
+ const vendorData = await (0, load_data_1.default)('vendor.yml', opts.offline).then(js_yaml_1.default.load);
25
+ const docData = await (0, load_data_1.default)('documentation.yml', opts.offline).then(js_yaml_1.default.load);
26
+ const heuristicsData = await (0, load_data_1.default)('heuristics.yml', opts.offline).then(js_yaml_1.default.load);
27
+ const generatedData = await (0, load_data_1.default)('generated.rb', opts.offline).then(text => { var _a; return (_a = text.match(/(?<=name\.match\(\/).+?(?=(?<!\\)\/)/gm)) !== null && _a !== void 0 ? _a : []; });
28
28
  const vendorPaths = [...vendorData, ...docData, ...generatedData];
29
29
  // Setup main variables
30
30
  const fileAssociations = {};
@@ -86,8 +86,9 @@ async function analyse(input, opts = {}) {
86
86
  if (!useRawContent && opts.checkAttributes) {
87
87
  for (const folder of folders) {
88
88
  // Skip if folder is marked in gitattributes
89
- if (relPath(folder) && gitignores.ignores(relPath(folder)))
89
+ if (relPath(folder) && gitignores.ignores(relPath(folder))) {
90
90
  continue;
91
+ }
91
92
  // Parse gitignores
92
93
  const ignoresFile = path_1.default.join(folder, '.gitignore');
93
94
  if (opts.checkIgnored && fs_1.default.existsSync(ignoresFile)) {
@@ -101,10 +102,12 @@ async function analyse(input, opts = {}) {
101
102
  // Explicit text/binary associations
102
103
  const contentTypeMatches = attributesData.matchAll(/^(\S+).*?(-?binary|-?text)(?!=auto)/gm);
103
104
  for (const [_line, path, type] of contentTypeMatches) {
104
- if (['text', '-binary'].includes(type))
105
+ if (['text', '-binary'].includes(type)) {
105
106
  customText.add(path);
106
- if (['-text', 'binary'].includes(type))
107
+ }
108
+ if (['-text', 'binary'].includes(type)) {
107
109
  customBinary.add(path);
110
+ }
108
111
  }
109
112
  // Custom vendor options
110
113
  const vendorMatches = attributesData.matchAll(/^(\S+).*[^-]linguist-(vendored|generated|documentation)(?!=false)/gm);
@@ -117,8 +120,9 @@ async function analyse(input, opts = {}) {
117
120
  // If specified language is an alias, associate it with its full name
118
121
  if (!langData[forcedLang]) {
119
122
  const overrideLang = Object.entries(langData).find(entry => { var _a; return (_a = entry[1].aliases) === null || _a === void 0 ? void 0 : _a.includes(forcedLang.toLowerCase()); });
120
- if (overrideLang)
123
+ if (overrideLang) {
121
124
  forcedLang = overrideLang[0];
125
+ }
122
126
  }
123
127
  const fullPath = relPath(folder) + '/' + path;
124
128
  overrides[fullPath] = forcedLang;
@@ -154,11 +158,11 @@ async function analyse(input, opts = {}) {
154
158
  if (useRawContent) {
155
159
  firstLine = (_e = (_d = (_c = opts.fileContent) === null || _c === void 0 ? void 0 : _c[files.indexOf(file)]) === null || _d === void 0 ? void 0 : _d.split('\n')[0]) !== null && _e !== void 0 ? _e : null;
156
160
  }
157
- else {
158
- if (!fs_1.default.existsSync(file) || fs_1.default.lstatSync(file).isDirectory())
159
- continue;
161
+ else if (fs_1.default.existsSync(file) && !fs_1.default.lstatSync(file).isDirectory()) {
160
162
  firstLine = await (0, read_file_1.default)(file, true).catch(() => null);
161
163
  }
164
+ else
165
+ continue;
162
166
  // Skip if file is unreadable
163
167
  if (firstLine === null)
164
168
  continue;
@@ -175,10 +179,11 @@ async function analyse(input, opts = {}) {
175
179
  const matchesLang = firstLine.toLowerCase().match(langMatcher(lang));
176
180
  const matchesAlias = (_g = data.aliases) === null || _g === void 0 ? void 0 : _g.some(lang => firstLine.toLowerCase().match(langMatcher(lang)));
177
181
  // Add language
178
- if (opts.checkShebang && matchesInterpretor)
179
- matches.push(lang);
180
- if (opts.checkModeline && (matchesLang || matchesAlias))
182
+ const interpretorCheck = opts.checkShebang && matchesInterpretor;
183
+ const modelineCheck = opts.checkModeline && (matchesLang || matchesAlias);
184
+ if (interpretorCheck || modelineCheck) {
181
185
  matches.push(lang);
186
+ }
182
187
  }
183
188
  if (matches.length) {
184
189
  // Add explicitly-identified language
@@ -213,12 +218,14 @@ async function analyse(input, opts = {}) {
213
218
  for (const lang in langData) {
214
219
  // Check if extension is a match
215
220
  const matchesExt = (_j = langData[lang].extensions) === null || _j === void 0 ? void 0 : _j.some(ext => file.toLowerCase().endsWith(ext.toLowerCase()));
216
- if (matchesExt)
221
+ if (matchesExt) {
217
222
  addResult(file, lang);
223
+ }
218
224
  }
219
225
  // Fallback to null if no language matches
220
- if (!fileAssociations[file])
226
+ if (!fileAssociations[file]) {
221
227
  addResult(file, null);
228
+ }
222
229
  }
223
230
  // Narrow down file associations to the best fit
224
231
  for (const file in fileAssociations) {
@@ -280,16 +287,19 @@ async function analyse(input, opts = {}) {
280
287
  const categories = ['data', 'markup', 'programming', 'prose'];
281
288
  const hiddenCategories = categories.filter(cat => !opts.categories.includes(cat));
282
289
  for (const [file, lang] of Object.entries(results.files.results)) {
283
- if (!hiddenCategories.some(cat => { var _a; return lang && ((_a = langData[lang]) === null || _a === void 0 ? void 0 : _a.type) === cat; }))
290
+ if (!hiddenCategories.some(cat => { var _a; return lang && ((_a = langData[lang]) === null || _a === void 0 ? void 0 : _a.type) === cat; })) {
284
291
  continue;
292
+ }
285
293
  delete results.files.results[file];
286
- if (lang)
294
+ if (lang) {
287
295
  delete results.languages.results[lang];
296
+ }
288
297
  }
289
298
  for (const category of hiddenCategories) {
290
299
  for (const [lang, { type }] of Object.entries(results.languages.results)) {
291
- if (type === category)
300
+ if (type === category) {
292
301
  delete results.languages.results[lang];
302
+ }
293
303
  }
294
304
  }
295
305
  }
@@ -298,8 +308,9 @@ async function analyse(input, opts = {}) {
298
308
  const newMap = {};
299
309
  for (const [file, lang] of Object.entries(results.files.results)) {
300
310
  let relPath = path_1.default.relative(process.cwd(), file).replace(/\\/g, '/');
301
- if (!relPath.startsWith('../'))
311
+ if (!relPath.startsWith('../')) {
302
312
  relPath = './' + relPath;
313
+ }
303
314
  newMap[relPath] = lang;
304
315
  }
305
316
  results.files.results = newMap;
@@ -323,8 +334,9 @@ async function analyse(input, opts = {}) {
323
334
  // Add language and bytes data to corresponding section
324
335
  const { type } = langData[lang];
325
336
  (_s = (_v = results.languages.results)[lang]) !== null && _s !== void 0 ? _s : (_v[lang] = { type, bytes: 0, color: langData[lang].color });
326
- if (opts.childLanguages)
337
+ if (opts.childLanguages) {
327
338
  results.languages.results[lang].parent = langData[lang].group;
339
+ }
328
340
  results.languages.results[lang].bytes += fileSize;
329
341
  results.languages.bytes += fileSize;
330
342
  }
package/dist/types.d.ts CHANGED
@@ -14,6 +14,7 @@ export interface Options {
14
14
  relativePaths?: boolean;
15
15
  childLanguages?: boolean;
16
16
  quick?: boolean;
17
+ offline?: boolean;
17
18
  checkIgnored?: boolean;
18
19
  checkAttributes?: boolean;
19
20
  checkHeuristics?: boolean;
@@ -0,0 +1,17 @@
1
+ - ^[Dd]ocs?/
2
+ - (^|/)[Dd]ocumentation/
3
+ - (^|/)[Gg]roovydoc/
4
+ - (^|/)[Jj]avadoc/
5
+ - ^[Mm]an/
6
+ - ^[Ee]xamples/
7
+ - ^[Dd]emos?/
8
+ - (^|/)inst/doc/- (^|/)CITATION(\.cff|(S)?(\.(bib|md))?)$
9
+ - (^|/)CHANGE(S|LOG)?(\.|$)
10
+ - (^|/)CONTRIBUTING(\.|$)
11
+ - (^|/)COPYING(\.|$)
12
+ - (^|/)INSTALL(\.|$)
13
+ - (^|/)LICEN[CS]E(\.|$)
14
+ - (^|/)[Ll]icen[cs]e(\.|$)
15
+ - (^|/)README(\.|$)
16
+ - (^|/)[Rr]eadme(\.|$)
17
+ - ^[Ss]amples?/
@@ -0,0 +1,351 @@
1
+ module Linguist
2
+ class Generated
3
+ def self.generated?(name, data)
4
+ new(name, data).generated?
5
+ end
6
+ def initialize(name, data)
7
+ @name = name
8
+ @extname = File.extname(name)
9
+ @_data = data
10
+ end attr_reader :name, :extname
11
+ def data
12
+ @data ||= @_data.respond_to?(:call) ? @_data.call() : @_data
13
+ end
14
+ def lines
15
+ @lines ||= data ? data.split("\n", -1) : []
16
+ end
17
+ def generated?
18
+ xcode_file? ||
19
+ intellij_file? ||
20
+ cocoapods? ||
21
+ carthage_build? ||
22
+ generated_graphql_relay? ||
23
+ generated_net_designer_file? ||
24
+ generated_net_specflow_feature_file? ||
25
+ composer_lock? ||
26
+ cargo_lock? ||
27
+ node_modules? ||
28
+ go_vendor? ||
29
+ go_lock? ||
30
+ poetry_lock? ||
31
+ esy_lock? ||
32
+ npm_shrinkwrap_or_package_lock? ||
33
+ terraform_lock? ||
34
+ generated_yarn_plugnplay? ||
35
+ godeps? ||
36
+ generated_by_zephir? ||
37
+ minified_files? ||
38
+ has_source_map? ||
39
+ source_map? ||
40
+ compiled_coffeescript? ||
41
+ generated_parser? ||
42
+ generated_net_docfile? ||
43
+ generated_postscript? ||
44
+ compiled_cython_file? ||
45
+ pipenv_lock? ||
46
+ generated_go? ||
47
+ generated_protocol_buffer_from_go? ||
48
+ generated_protocol_buffer? ||
49
+ generated_javascript_protocol_buffer? ||
50
+ generated_apache_thrift? ||
51
+ generated_jni_header? ||
52
+ vcr_cassette? ||
53
+ generated_antlr? ||
54
+ generated_module? ||
55
+ generated_unity3d_meta? ||
56
+ generated_racc? ||
57
+ generated_jflex? ||
58
+ generated_grammarkit? ||
59
+ generated_roxygen2? ||
60
+ generated_html? ||
61
+ generated_jison? ||
62
+ generated_grpc_cpp? ||
63
+ generated_dart? ||
64
+ generated_perl_ppport_header? ||
65
+ generated_gamemakerstudio? ||
66
+ generated_gimp? ||
67
+ generated_visualstudio6? ||
68
+ generated_haxe? ||
69
+ generated_jooq? ||
70
+ generated_pascal_tlb?
71
+ end
72
+ def xcode_file?
73
+ ['.nib', '.xcworkspacedata', '.xcuserstate'].include?(extname)
74
+ end
75
+ def intellij_file?
76
+ !!name.match(/(?:^|\/)\.idea\//)
77
+ end
78
+ def cocoapods?
79
+ !!name.match(/(^Pods|\/Pods)\//)
80
+ end
81
+ def carthage_build?
82
+ !!name.match(/(^|\/)Carthage\/Build\//)
83
+ end
84
+ def maybe_minified?
85
+ ['.js', '.css'].include? extname.downcase
86
+ end
87
+ def minified_files?
88
+ if maybe_minified? and lines.any?
89
+ (lines.inject(0) { |n, l| n += l.length } / lines.length) > 110
90
+ else
91
+ false
92
+ end
93
+ end
94
+ def has_source_map?
95
+ return false unless maybe_minified?
96
+ lines.last(2).any? { |l| l.match(/^\/[*\/][\#@] source(?:Mapping)?URL|sourceURL=/) }
97
+ end
98
+ def source_map?
99
+ return false unless extname.downcase == '.map' name =~ /(\.css|\.js)\.map$/i ||
100
+ lines[0] =~ /^{"version":\d+,/ ||
101
+ lines[0] =~ /^\/\*\* Begin line maps\. \*\*\/{/
102
+ end
103
+ def compiled_coffeescript?
104
+ return false unless extname == '.js'
105
+ if lines[0] =~ /^\/\/ Generated by /
106
+ return true
107
+ end if lines[0] == '(function() {' &&
108
+ lines[-2] == '}).call(this);' &&
109
+ lines[-1] == '' score = 0 lines.each do |line|
110
+ if line =~ /var /
111
+ score += 1 * line.gsub(/(_fn|_i|_len|_ref|_results)/).count
112
+ score += 3 * line.gsub(/(__bind|__extends|__hasProp|__indexOf|__slice)/).count
113
+ end
114
+ end
115
+ score >= 3
116
+ else
117
+ false
118
+ end
119
+ end
120
+ def generated_net_docfile?
121
+ return false unless extname.downcase == ".xml"
122
+ return false unless lines.count > 3
123
+ return lines[1].include?("<doc>") &&
124
+ lines[2].include?("<assembly>") &&
125
+ lines[-2].include?("</doc>")
126
+ end
127
+ def generated_net_designer_file?
128
+ !!name.match(/\.designer\.(cs|vb)$/i)
129
+ end
130
+ def generated_net_specflow_feature_file?
131
+ !!name.match(/\.feature\.cs$/i)
132
+ end
133
+ def generated_parser?
134
+ return false unless extname == '.js'
135
+ if lines[0..4].join('') =~ /^(?:[^\/]|\/[^\*])*\/\*(?:[^\*]|\*[^\/])*Generated by PEG.js/
136
+ return true
137
+ end false
138
+ end
139
+ def generated_postscript?
140
+ return false unless ['.ps', '.eps', '.pfa'].include? extname
141
+ return true if data =~ /(\n|\r\n|\r)\s*(?:currentfile eexec\s+|\/sfnts\s+\[\1<)\h{8,}\1/
142
+ creator = lines[0..9].find {|line| line =~ /^%%Creator: /}
143
+ return false if creator.nil?
144
+ return true if creator =~ /[0-9]|draw|mpage|ImageMagick|inkscape|MATLAB/ ||
145
+ creator =~ /PCBNEW|pnmtops|\(Unknown\)|Serif Affinity|Filterimage -tops/
146
+ !!creator.include?("EAGLE") and lines[0..4].find {|line| line =~ /^%%Title: EAGLE Drawing /}
147
+ end def generated_go?
148
+ return false unless extname == '.go'
149
+ return false unless lines.count > 1 return lines.first(40).any? { |l| l.include? "Code generated by" }
150
+ end
151
+ def generated_protocol_buffer_from_go?
152
+ return false unless extname == '.proto'
153
+ return false unless lines.count > 1 return lines.first(20).any? { |l| l.include? "This file was autogenerated by go-to-protobuf" }
154
+ end PROTOBUF_EXTENSIONS = ['.py', '.java', '.h', '.cc', '.cpp', '.m', '.rb', '.php']
155
+ def generated_protocol_buffer?
156
+ return false unless PROTOBUF_EXTENSIONS.include?(extname)
157
+ return false unless lines.count > 1 return lines.first(3).any? { |l| l.include?("Generated by the protocol buffer compiler. DO NOT EDIT!") }
158
+ end
159
+ def generated_javascript_protocol_buffer?
160
+ return false unless extname == ".js"
161
+ return false unless lines.count > 6 return lines[5].include?("GENERATED CODE -- DO NOT EDIT!")
162
+ end APACHE_THRIFT_EXTENSIONS = ['.rb', '.py', '.go', '.js', '.m', '.java', '.h', '.cc', '.cpp', '.php']
163
+ def generated_apache_thrift?
164
+ return false unless APACHE_THRIFT_EXTENSIONS.include?(extname)
165
+ return lines.first(6).any? { |l| l.include?("Autogenerated by Thrift Compiler") }
166
+ end
167
+ def generated_jni_header?
168
+ return false unless extname == '.h'
169
+ return false unless lines.count > 2 return lines[0].include?("/* DO NOT EDIT THIS FILE - it is machine generated */") &&
170
+ lines[1].include?("#include <jni.h>")
171
+ end
172
+ def node_modules?
173
+ !!name.match(/node_modules\//)
174
+ end
175
+ def go_vendor?
176
+ !!name.match(/vendor\/((?!-)[-0-9A-Za-z]+(?<!-)\.)+(com|edu|gov|in|me|net|org|fm|io)/)
177
+ end
178
+ def go_lock?
179
+ !!name.match(/(Gopkg|glide)\.lock/)
180
+ end
181
+ def poetry_lock?
182
+ !!name.match(/poetry\.lock/)
183
+ end
184
+ def esy_lock?
185
+ !!name.match(/(^|\/)(\w+\.)?esy.lock$/)
186
+ end
187
+ def npm_shrinkwrap_or_package_lock?
188
+ !!name.match(/npm-shrinkwrap\.json/) || !!name.match(/package-lock\.json/)
189
+ end
190
+ def generated_yarn_plugnplay?
191
+ !!name.match(/(^|\/)\.pnp\..*$/)
192
+ end
193
+ def godeps?
194
+ !!name.match(/Godeps\//)
195
+ end
196
+ def composer_lock?
197
+ !!name.match(/composer\.lock/)
198
+ end
199
+ def generated_by_zephir?
200
+ !!name.match(/.\.zep\.(?:c|h|php)$/)
201
+ end
202
+ def cargo_lock?
203
+ !!name.match(/Cargo\.lock/)
204
+ end
205
+ def vcr_cassette?
206
+ return false unless extname == '.yml'
207
+ return false unless lines.count > 2
208
+ return lines[-2].include?("recorded_with: VCR")
209
+ end
210
+ def generated_antlr?
211
+ return false unless extname == '.g'
212
+ return false unless lines.count > 2
213
+ return lines[1].include?("generated by Xtest")
214
+ end
215
+ def compiled_cython_file?
216
+ return false unless ['.c', '.cpp'].include? extname
217
+ return false unless lines.count > 1
218
+ return lines[0].include?("Generated by Cython")
219
+ end
220
+ def pipenv_lock?
221
+ !!name.match(/Pipfile\.lock/)
222
+ end
223
+ def terraform_lock?
224
+ !!name.match(/(?:^|\/)\.terraform\.lock\.hcl$/)
225
+ end
226
+ def generated_module?
227
+ return false unless extname == '.mod'
228
+ return false unless lines.count > 1
229
+ return lines[0].include?("PCBNEW-LibModule-V") ||
230
+ lines[0].include?("GFORTRAN module version '")
231
+ end
232
+ def generated_unity3d_meta?
233
+ return false unless extname == '.meta'
234
+ return false unless lines.count > 1
235
+ return lines[0].include?("fileFormatVersion: ")
236
+ end
237
+ def generated_racc?
238
+ return false unless extname == '.rb'
239
+ return false unless lines.count > 2
240
+ return lines[2].start_with?("# This file is automatically generated by Racc")
241
+ end
242
+ def generated_jflex?
243
+ return false unless extname == '.java'
244
+ return false unless lines.count > 1
245
+ return lines[0].start_with?("/* The following code was generated by JFlex ")
246
+ end
247
+ def generated_grammarkit?
248
+ return false unless extname == '.java'
249
+ return false unless lines.count > 1
250
+ return lines[0].start_with?("// This is a generated file. Not intended for manual editing.")
251
+ end
252
+ def generated_roxygen2?
253
+ return false unless extname == '.Rd'
254
+ return false unless lines.count > 1 return lines[0].include?("% Generated by roxygen2: do not edit by hand")
255
+ end
256
+ def generated_jison?
257
+ return false unless extname == '.js'
258
+ return false unless lines.count > 1
259
+ return lines[0].start_with?("/* parser generated by jison ") ||
260
+ lines[0].start_with?("/* generated by jison-lex ")
261
+ end
262
+ def generated_grpc_cpp?
263
+ return false unless %w{.cpp .hpp .h .cc}.include? extname
264
+ return false unless lines.count > 1
265
+ return lines[0].start_with?("// Generated by the gRPC")
266
+ end
267
+ def generated_dart?
268
+ return false unless extname == '.dart'
269
+ return false unless lines.count > 1
270
+ return lines.first.downcase =~ /generated code\W{2,3}do not modify/
271
+ end
272
+ def generated_perl_ppport_header?
273
+ return false unless name.match(/ppport\.h$/)
274
+ return false unless lines.count > 10
275
+ return lines[8].include?("Automatically created by Devel::PPPort")
276
+ end
277
+ def generated_graphql_relay?
278
+ !!name.match(/__generated__\//)
279
+ end
280
+ def generated_gamemakerstudio?
281
+ return false unless ['.yy', '.yyp'].include? extname
282
+ return false unless lines.count > 3
283
+ return lines[2].match(/\"modelName\"\:\s*\"GM/) ||
284
+ lines[0] =~ /^\d\.\d\.\d.+\|\{/
285
+ end
286
+ def generated_gimp?
287
+ return false unless ['.c', '.h'].include? extname
288
+ return false unless lines.count > 0
289
+ return lines[0].match(/\/\* GIMP [a-zA-Z0-9\- ]+ C\-Source image dump \(.+?\.c\) \*\//) ||
290
+ lines[0].match(/\/\* GIMP header image file format \([a-zA-Z0-9\- ]+\)\: .+?\.h \*\//)
291
+ end
292
+ def generated_visualstudio6?
293
+ return false unless extname.downcase == '.dsp'
294
+ lines.first(3).any? { |l| l.include? '# Microsoft Developer Studio Generated Build File' }
295
+ end HAXE_EXTENSIONS = ['.js', '.py', '.lua', '.cpp', '.h', '.java', '.cs', '.php']
296
+ def generated_haxe?
297
+ return false unless HAXE_EXTENSIONS.include?(extname)
298
+ return lines.first(3).any? { |l| l.include?("Generated by Haxe") }
299
+ end
300
+ def generated_html?
301
+ return false unless ['.html', '.htm', '.xhtml'].include? extname.downcase
302
+ return false unless lines.count > 1
303
+ return true if lines[0..1].any? do |line|
304
+ line.match(/<!-- Generated by pkgdown: do not edit by hand -->/)
305
+ end
306
+ return true if lines.count > 2 && lines[2].start_with?('<!-- This is an automatically generated file.')
307
+ return true if lines[0..30].any? do |line|
308
+ line.match(/<!--\s+Generated by Doxygen\s+[.0-9]+\s*-->/i)
309
+ end
310
+ matches = lines[0..30].join(' ').scan(/<meta(\s+[^>]++)>/i)
311
+ return false if matches.empty?
312
+ return matches.map {|x| extract_html_meta(x) }.any? do |attr|
313
+ attr["name"].to_s.downcase == 'generator' &&
314
+ [attr["content"], attr["value"]].any? do |cv|
315
+ !cv.nil? &&
316
+ cv.match(/^
317
+ ( org \s+ mode
318
+ | j?latex2html
319
+ | groff
320
+ | makeinfo
321
+ | texi2html
322
+ | ronn
323
+ ) \b
324
+ /ix)
325
+ end
326
+ end
327
+ end
328
+ def generated_jooq?
329
+ return false unless extname.downcase == '.java'
330
+ lines.first(2).any? { |l| l.include? 'This file is generated by jOOQ.' }
331
+ end
332
+ def generated_pascal_tlb?
333
+ !!name.match(/_tlb\.pas$/i)
334
+ end
335
+ def extract_html_meta(match)
336
+ (match.last.sub(/\/\Z/, "").strip.scan(/
337
+ (?<=^|\s)
338
+ (name|content|value)
339
+ \s* = \s*
340
+ ( "[^"]+"
341
+ | '[^']+'
342
+ | [^\s"']+
343
+ )
344
+ /ix)).map do |match|
345
+ key = match[0].downcase
346
+ val = match[1].gsub(/\A["']|["']\Z/, '')
347
+ [key, val]
348
+ end.select { |x| x.length == 2 }.to_h
349
+ end
350
+ end
351
+ end