deno_lint icon indicating copy to clipboard operation
deno_lint copied to clipboard

Automate acquisition of global properties

Open jsejcksn opened this issue 3 years ago • 4 comments

#419 was just fixed by #421, adding AggregateError to the globals list: https://github.com/denoland/deno_lint/blob/master/src/globals.rs

However, I noticed that there are some other inconsistencies between that list and what is produced by enumerating the list in Deno:

const caseInsensitiveSort = (a: string, b: string): number => {
  const lowerA = a.toLowerCase();
  const lowerB = b.toLowerCase();
  if (lowerA < lowerB) return -1;
  if (lowerA > lowerB) return 1;
  return 0;
};

const globals = [...Object.getOwnPropertyNames(globalThis)];

console.log(globals.sort(caseInsensitiveSort).join('\n'));
Which produces this list in Deno 1.4.6:
AbortController
AbortSignal
addEventListener
AggregateError
Array
ArrayBuffer
atob
Atomics
BigInt
BigInt64Array
BigUint64Array
Blob
Boolean
btoa
ByteLengthQueuingStrategy
clearInterval
clearTimeout
close
closed
CloseEvent
console
CountQueuingStrategy
crypto
CustomEvent
DataView
Date
decodeURI
decodeURIComponent
Deno
dispatchEvent
DOMException
encodeURI
encodeURIComponent
Error
ErrorEvent
escape
eval
EvalError
Event
EventTarget
fetch
File
FileReader
FinalizationRegistry
Float32Array
Float64Array
FormData
Function
globalThis
Headers
Infinity
Int16Array
Int32Array
Int8Array
isFinite
isNaN
JSON
Map
Math
MessageEvent
NaN
Number
Object
onload
onunload
parseFloat
parseInt
Performance
performance
PerformanceEntry
PerformanceMark
PerformanceMeasure
ProgressEvent
Promise
Proxy
queueMicrotask
RangeError
ReadableStream
ReferenceError
Reflect
RegExp
removeEventListener
Request
Response
self
Set
setInterval
setTimeout
SharedArrayBuffer
String
Symbol
SyntaxError
TextDecoder
TextEncoder
TransformStream
TypeError
Uint16Array
Uint32Array
Uint8Array
Uint8ClampedArray
undefined
unescape
URIError
URL
URLSearchParams
WeakMap
WeakRef
WeakSet
WebAssembly
WebSocket
window
Worker
WritableStream

How is src/globals.rs being maintained? Is it a manual process? Can it be automated?

jsejcksn avatar Oct 20 '20 14:10 jsejcksn

@jsejcksn thanks for the report, it is indeed a problem. I did an update to the list based on your findings in https://github.com/denoland/deno_lint/pull/426. Unfortunately I see no clear way to automate this process at the moment, so I'm gonna keep the issue open

bartlomieju avatar Oct 20 '20 16:10 bartlomieju

@bartlomieju I see there are some entries in globals.rs which are not produced using the method above. I tried to access a couple of them and got undefined. Where are they coming from / why are they in the list?

jsejcksn avatar Oct 20 '20 17:10 jsejcksn

@jsejcksn could you list those? I've checked the list when I added it originally so I'm a bit surprised something is undefined.

bartlomieju avatar Oct 20 '20 17:10 bartlomieju

@bartlomieju See item 3 below. Here's a script that will

  • fetch the current source and save to ./globals-src.rs
  • create an updated source from the computed globals and save to ./globals-computed.rs
  • parse each list and log the types associated to the exclusive values (see output)
globals.ts:
// deno run --allow-net="raw.githubusercontent.com" --allow-read="globals-computed.rs","globals-src.rs" --allow-write="globals-computed.rs","globals-src.rs" globals.ts

const indent = '  ';
const newline = '\n';
const computedPath = 'globals-computed.rs';
const srcPath = 'globals-src.rs';

const srcPreList = `// Copyright 2020 the Deno authors. All rights reserved. MIT license.

/// List of globals available in Deno environment
///
/// Adapted from https://www.npmjs.com/package/globals
#[allow(unused)]
pub static GLOBALS: &[&str] = &[${newline}`;

const srcPostList = `];${newline}`;

const fetchSource = async (): Promise<string> => {
  const url = 'https://raw.githubusercontent.com/denoland/deno_lint/master/src/globals.rs';
  const response = await fetch(url);
  if (!response.ok) throw new Error('Response not OK');
  return response.text();
};

const caseInsensitiveSort = (a: string, b: string): number => {
  const lowerA = a.toLowerCase();
  const lowerB = b.toLowerCase();
  if (lowerA < lowerB) return -1;
  if (lowerA > lowerB) return 1;
  return 0;
};

const getComputedGlobals = (): string[] => {
  const globals = [...Object.getOwnPropertyNames(globalThis)];
  return globals.sort(caseInsensitiveSort);
};

const updateSourceFiles = async () => {
  const globals = getComputedGlobals();

  const listReducer = (
    string: string,
    value: string,
  ) => `${string}${indent}"${value}",${newline}`;

  const list = globals.reduce(listReducer, '');

  const result = `${srcPreList}${list}${srcPostList}`;
  await Deno.writeTextFile(computedPath, result);

  const src = await fetchSource();
  await Deno.writeTextFile(srcPath, src);
};

const parseGlobalsFromSource = async (src: string): Promise<string[]> => {
  const listItemRegex = / {2}"\w+",/gu;
  const globals = (await Deno.readTextFile(src))
    .match(listItemRegex)
    ?.map(str => str.slice(3, -2)) ?? [];
  return globals;
};

const main = async () => {
  await updateSourceFiles();
  const srcGlobals = new Set(await parseGlobalsFromSource(srcPath));
  const computedGlobals = new Set(await parseGlobalsFromSource(computedPath));
  const commonGlobals = new Set<string>();

  for (const value of srcGlobals.values()) {
    if (computedGlobals.has(value)) commonGlobals.add(value);
  }

  for (const value of commonGlobals.values()) {
    computedGlobals.delete(value);
    srcGlobals.delete(value);
  }

  const listReducer = (
    string: string,
    value: string,
  // deno-lint-ignore no-explicit-any
  ) => `${string}${indent}${value}: ${typeof (globalThis as any)[value]}${newline}`;

  console.log(`Exclusive computed:${newline}${[...computedGlobals.values()]
    .reduce(listReducer, '')}`);

  console.log(`Exclusive source:${newline}${[...srcGlobals.values()]
    .reduce(listReducer, '')}`);
};

if (import.meta.main) main();

output:

Exclusive computed:
  ByteLengthQueuingStrategy: function
  MessageEvent: function
  onload: object
  onunload: object

Exclusive source:
  constructor: function
  event: undefined
  EventSource: undefined
  hasOwnProperty: function
  isPrototypeOf: function
  onmessage: undefined
  onmessageerror: undefined
  Permissions: undefined
  PermissionStatus: undefined
  postMessage: undefined
  propertyIsEnumerable: function
  toLocaleString: function
  toString: function
  valueOf: function
  Window: undefined

jsejcksn avatar Oct 20 '20 18:10 jsejcksn