typia icon indicating copy to clipboard operation
typia copied to clipboard

Support dynamic property names

Open j-mendez opened this issue 2 years ago • 6 comments

Bug Report

Summary

I am trying to call the TS.stringify on a lhr object and getting the following error key cannot contain .``. The lighthouse report contains an object with a property called icuMessagePaths with keys that contains a . ex: 'lighthouse-core/audits/is-on-https.js | title': [Array]. Is it possible that this will be handled in the future to allow parsing symbols in the key property names?

const data = {
  requestedUrl: 'https://jeffmendez.com/about',
  finalUrl: 'https://jeffmendez.com/about/',
  lighthouseVersion: '9.6.2',
  userAgent: 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/102.0.5005.115 Safari/537.36',
  fetchTime: '2022-07-15T12:19:17.077Z',
  environment: {
    networkUserAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4695.0 Safari/537.36 Chrome-Lighthouse',
    hostUserAgent: 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/102.0.5005.115 Safari/537.36',
    benchmarkIndex: 1246.5
  },
  runWarnings: [
    'The page may not be loading as expected because your test URL (https://jeffmendez.com/about) was redirected to https://jeffmendez.com/about/. Try testing the second URL directly.'
  ],
  timing: { total: 12460 },
  i18n: {
    icuMessagePaths: {
      'lighthouse-core/audits/is-on-https.js | title': [Array],
      'lighthouse-core/audits/is-on-https.js | description': [Array],
      'lighthouse-core/audits/viewport.js | title': [Array],
      'lighthouse-core/audits/viewport.js | description': [Array],
      'lighthouse-core/lib/i18n/i18n.js | firstContentfulPaintMetric': [Array],
      'lighthouse-core/audits/metrics/first-contentful-paint.js | description': [Array],
      'lighthouse-core/lib/i18n/i18n.js | seconds': [Array],
      'lighthouse-core/lib/i18n/i18n.js | largestContentfulPaintMetric': [Array],
      'lighthouse-core/audits/metrics/largest-contentful-paint.js | description': [Array],
      'lighthouse-core/lib/i18n/i18n.js | firstMeaningfulPaintMetric': [Array],
      'lighthouse-core/audits/metrics/first-meaningful-paint.js | description': [Array],
      'lighthouse-core/lib/i18n/i18n.js | speedIndexMetric': [Array],
      'lighthouse-core/audits/metrics/speed-index.js | description': [Array],
      'lighthouse-core/lib/i18n/i18n.js | totalBlockingTimeMetric': [Array],
      'lighthouse-core/audits/metrics/total-blocking-time.js | description': [Array],
      'lighthouse-core/lib/i18n/i18n.js | ms': [Array],
      'lighthouse-core/lib/i18n/i18n.js | maxPotentialFIDMetric': [Array],
      'lighthouse-core/audits/metrics/max-potential-fid.js | description': [Array],
      'lighthouse-core/lib/i18n/i18n.js | cumulativeLayoutShiftMetric': [Array],
      }
  }
}

The schema defs

/**
 * @license Copyright 2018 The Lighthouse Authors. All Rights Reserved.
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
 */

import { Result as AuditResult } from "./audit-result";
import { ConfigSettings } from "./settings";

/**
 * The full output of a Lighthouse run.
 */
interface Result {
  /** Gather mode used to collect artifacts for this result. */
  gatherMode: Result.GatherMode;
  /** The URL that Lighthouse initially navigated to. Will be `undefined` in timespan/snapshot. */
  requestedUrl?: string;
  /** The post-redirects URL that Lighthouse loaded. */
  finalUrl: string;
  /** The ISO-8601 timestamp of when the results were generated. */
  fetchTime: string;
  /** The version of Lighthouse with which these results were generated. */
  lighthouseVersion: string;
  /** An object containing the results of the audits, keyed by the audits' `id` identifier. */
  audits: Record<string, AuditResult>;
  /** The top-level categories, their overall scores, and member audits. */
  categories: Record<string, Result.Category>;
  /** Descriptions of the groups referenced by CategoryMembers. */
  categoryGroups?: Record<string, Result.ReportGroup>;

  /** The config settings used for these results. */
  configSettings: ConfigSettings;
  /** List of top-level warnings for this Lighthouse run. */
  runWarnings: string[];
  /** A top-level error message that, if present, indicates a serious enough problem that this Lighthouse result may need to be discarded. */
  runtimeError?: { code: string; message: string };
  /** The User-Agent string of the browser used run Lighthouse for these results. */
  userAgent: string;
  /** Information about the environment in which Lighthouse was run. */
  environment: Result.Environment;
  /** Execution timings for the Lighthouse run */
  timing: Result.Timing;
  /** Strings for the report and the record of all formatted string locations in the LHR and their corresponding source values. */
  i18n: {
    rendererFormattedStrings: Record<string, string>;
    /** Optional because LR has many old LHRs that return nothing for this property. */
    icuMessagePaths?: Result.IcuMessagePaths;
  };
  /** An array containing the result of all stack packs. */
  stackPacks?: Result.StackPack[];
}

// Result namespace
declare module Result {
  interface Environment {
    /** The user agent string of the version of Chrome used. */
    hostUserAgent: string;
    /** The user agent string that was sent over the network. */
    networkUserAgent: string;
    /** The benchmark index number that indicates rough device class. */
    benchmarkIndex: number;
    /** Many benchmark indexes. */
    benchmarkIndexes?: number[];
    /** The version of libraries with which these results were generated. Ex: axe-core. */
    credits?: Record<string, string | undefined>;
  }

  interface Timing {
    entries: MeasureEntry[];
    total: number;
  }

  interface MeasureEntry {
    // From PerformanceEntry
    readonly duration: number;
    readonly entryType: string;
    readonly name: string;
    readonly startTime: number;
    /** Whether timing entry was collected during artifact gathering. */
    gather?: boolean;
  }

  interface Category {
    /** The string identifier of the category. */
    id: string;
    /** The human-friendly name of the category */
    title: string;
    /** A more detailed description of the category and its importance. */
    description?: string;
    /** A description for the manual audits in the category. */
    manualDescription?: string;
    /** The overall score of the category, the weighted average of all its audits. */
    score: number | null;
    /** An array of references to all the audit members of this category. */
    auditRefs: AuditRef[];
  }

  interface AuditRef {
    /** Matches the `id` of an Audit.Result. */
    id: string;
    /** The weight of the audit's score in the overall category score. */
    weight: number;
    /** Optional grouping within the category. Matches the key of a Result.Group. */
    group?: string;
    /** The conventional acronym for the audit/metric. */
    acronym?: string;
    /** Any audit IDs closely relevant to this one. */
    relevantAudits?: string[];
  }

  interface ReportGroup {
    /** The title of the display group. */
    title: string;
    /** A brief description of the purpose of the display group. */
    description?: string;
  }

  /**
   * A pack of secondary audit descriptions to be used when a page uses a
   * specific technology stack, giving stack-specific advice for some of
   * Lighthouse's audits.
   */
  interface StackPack {
    /** The unique string ID for this stack pack. */
    id: string;
    /** The title of the stack pack, to be displayed in the report. */
    title: string;
    /** A base64 data url to be used as the stack pack's icon. */
    iconDataURL: string;
    /** A set of descriptions for some of Lighthouse's audits, keyed by audit `id`. */
    descriptions: Record<string, string>;
  }

  /**
   * Info about an `LH.IcuMessage` value that was localized to a string when
   * included in the LHR. Value is either a
   *  - path (`_.set()` style) into the original object where the message was replaced
   *  - those paths and a set of values that were inserted into the localized strings.
   */
  type IcuMessagePath =
    | string
    | { path: string; values: Record<string, string | number> };

  /**
   * A representation of `LH.IcuMessage`s that were in an object (e.g. `LH.Result`)
   * and have been replaced by localized strings. `LH.Result.IcuMessagePaths` provides a
   * mapping that can be used to change the locale of the object again.
   * Keyed by ids from the locale message json files, values are information on
   * how to replace the string if needed.
   */
  interface IcuMessagePaths {
    [i18nId: string]: IcuMessagePath[];
  }

  /** Gather mode used to collect artifacts. */
  type GatherMode = "navigation" | "timespan" | "snapshot";
}

export default Result;

My ts config:

{
  "compilerOptions": {
    "outDir": "dist",
    "target": "es6",
    "module": "commonjs",
    "lib": ["es6"],
    "sourceMap": true,
    "allowJs": true,
    "moduleResolution": "node",
    "noEmit": false,
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "suppressImplicitAnyIndexErrors": true,
    "noUnusedLocals": true,
    "allowSyntheticDefaultImports": true,
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "skipLibCheck": true,
    "resolveJsonModule": true,
    "removeComments": true,
    "baseUrl": ".",
    "paths": {
      "@app/*": ["./src/*"]
    },
    "strict": true,
    "plugins": [
      {
        "transform": "typescript-json/lib/transform"
      }
    ]
  },
  "include": ["src", "*.proto"],
  "exclude": ["node_modules"]
}

Error on TSON.stringify(): no transform has been configured. Configure the "tsconfig.json" file following the README.md#setup

Code occuring the bug

import TSON from "typescript-json";

const json = TSON.stringify<Result>(insight);
        
/* Demonstration code occuring the bug you're reporting */

j-mendez avatar Jul 15 '22 12:07 j-mendez

interface IcuMessagePaths {
    [i18nId: string]: IcuMessagePath[];
}

typescript-json is not supporting dynamic property.

When such dynamic property exists, TSON.stringify() becomes slow like the native JSON.stringify().

Therefore, I'm considering typescript-json should support it or not.

I hope to listen your idea about it.

samchon avatar Jul 15 '22 18:07 samchon

@samchon that is a very valid point. I was thinking about separating the calls for my use case into two and then stitching the two JSON strings together. For my case it's kinda simple since the last property is the only property that has . in the name. I wonder if we could have a feature that checks for this or and maybe reduces the speed a bit on parsing.

When the transformer finds a property that contains a " single-quote or double-quote allow opting into the token handling.

j-mendez avatar Jul 15 '22 18:07 j-mendez

https://github.com/samchon/typescript-json/blob/master/test/structures/ObjectLiteralProperty.ts

As you can see from the below structure, typescript-json is testing such special-charactered property name. Therefore, the dot word (.) is not important. The failure comes only by the dynamic property name. If such dynamic property has a significant type, it's possible to boosting JSON string conversion speed. However, the speed would not such dramatic like static property names.

However, such dynamic property names are formal TypeScript spec. Therefore, I need to support it. Supporting such dynamic property names would be done at next week, perhaps. Thanks for suggestion and I'll try out for the update.

samchon avatar Jul 15 '22 18:07 samchon

Also, such dynamic properties would be supported even in the runtime type checkers.

I've assigned this issue as v3.2 update.

samchon avatar Jul 15 '22 18:07 samchon

@samchon Love that!! Agreed on the points, in theory it should still be faster than using JSON.stringify until it gets to those properties ( making it a better choice to use still ).

Ps: off topic, I like your github profile bio!

j-mendez avatar Jul 15 '22 18:07 j-mendez

Sorry for late implementation. I will try this again in this Saturday.

samchon avatar Sep 12 '22 12:09 samchon

Now, those functions support dynamic properties. Only stringify() is left.

Current version is 3.3.4 and the next version supporting stringify() would be 3.3.5.

  • application()
  • assertEquals()
  • assertType()
  • equals()
  • is()
  • validate()
  • validateEquals()

samchon avatar Oct 03 '22 18:10 samchon

@j-mendez You've waited for a long time.

Since v3.3.6 update, typescript-json has fully supported the dynamic properties

samchon avatar Oct 04 '22 18:10 samchon

@samchon thank you for the version tag for the change and work done for this!!

j-mendez avatar Oct 04 '22 21:10 j-mendez