js-yaml#safeLoad TypeScript Examples

The following examples show how to use js-yaml#safeLoad. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: utils.ts    From next-basics with GNU General Public License v3.0 7 votes vote down vote up
safeLoadField = (value: string, field: string): any => {
  let result;
  try {
    result = safeLoad(value, {
      schema: JSON_SCHEMA,
      json: true,
    });
  } catch (e) {
    // eslint-disable-next-line no-console
    console.warn(value, `Illegal ${field}`);
    return {
      help: `${field} is error`,
      $$validateStatus: true,
    };
  }
  return result;
}
Example #2
Source File: TestProject.ts    From yarn-plugins with MIT License 6 votes vote down vote up
public static async setup(): Promise<TestProject> {
    const dir = await tmp.dir();
    const pluginBundles = await globby('packages/*/bundles/**/*.js', {
      cwd: PROJECT_DIR,
    });
    const plugins = pluginBundles.map((src) => ({
      src,
      name: '@yarnpkg/' + basename(src, extname(src)),
      dest: posix.join('.yarn', 'plugins', ...src.split(sep).slice(3)),
    }));

    for (const path of plugins) {
      await copy(join(PROJECT_DIR, path.src), join(dir.path, path.dest));
    }

    const yarnConfig = safeLoad(
      await readFile(join(PROJECT_DIR, '.yarnrc.yml'), 'utf8'),
    ) as Record<string, unknown>;

    // Create .yarnrc.yml
    await outputFile(
      join(dir.path, '.yarnrc.yml'),
      safeDump({
        yarnPath: join(PROJECT_DIR, yarnConfig.yarnPath as string),
        plugins: plugins.map((plugin) => ({
          path: plugin.dest,
          spec: plugin.name,
        })),
      }),
    );

    // Create package.json
    await outputJSON(join(dir.path, 'package.json'), {
      private: true,
      workspaces: ['packages/*'],
    });

    return new TestProject(dir);
  }
Example #3
Source File: yamlUtils.ts    From amazon-states-language-service with MIT License 6 votes vote down vote up
export function convertJsonSnippetToYaml(snippetText: string) {
    // Convert to YAML with indendation of 1 space
    return safeDump(safeLoad(snippetText), { indent: 1 })
        // Remove quotation marks
        .replace(/[']/g, '')
        .split('\n')
        // For each line replace left padding spaces with tabs
        .map(line => {
            if (line.length) {
                let numOfSpaces = 0

                // Count number of spaces that the line begins with
                for (const char of line) {
                    if (char === ' ') {
                        numOfSpaces++
                    } else {
                        break
                    }
                }

                // Convert each space to tab character. Even though tab carracters are not valid yaml whitespace characters
                // the vscode will convert them to the correct number of spaces according to user preferences.
                return '\t'.repeat(numOfSpaces) + line.slice(numOfSpaces, line.length)
            } else {
                return line
            }
        })
        .join('\n')
}
Example #4
Source File: index.tsx    From che-dashboard-next with Eclipse Public License 2.0 6 votes vote down vote up
private onChange(newValue: string, isValid: boolean): void {
    if (this.skipNextOnChange) {
      this.skipNextOnChange = false;
      return;
    }

    let devfile: che.WorkspaceDevfile;
    try {
      devfile = safeLoad(newValue);
    } catch (e) {
      console.error('DevfileEditor parse error', e);
      return;
    }
    this.props.onChange(devfile, isValid);
  }
Example #5
Source File: index.tsx    From che-dashboard-next with Eclipse Public License 2.0 6 votes vote down vote up
private async handleDevfileSelect(meta: che.DevfileMetaData): Promise<void> {
    // clear location input
    this.devfileLocationRef.current?.clearInput();
    try {
      const devfileContent = await this.props.requestDevfile(meta.links.self) as string;
      const devfile = safeLoad(devfileContent);
      this.props.onDevfile(devfile);
    } catch (e) {
      this.showAlert({
        key: 'load-devfile-failed',
        title: `Failed to load devfile. ${e}`,
        variant: AlertVariant.danger,
      });
    }
  }
Example #6
Source File: processor.ts    From next-basics with GNU General Public License v3.0 6 votes vote down vote up
export function yaml(value: string): unknown {
  let result;
  try {
    result = safeLoad(value, { schema: JSON_SCHEMA, json: true });
  } catch (e) {
    // eslint-disable-next-line no-console
    console.error(e);
  }
  return result;
}
Example #7
Source File: PrivateGitHubProvider.ts    From electron-differential-updater with MIT License 6 votes vote down vote up
async getLatestVersion(): Promise<PrivateGitHubUpdateInfo> {
    const cancellationToken = new CancellationToken()
    const channelFile = getChannelFilename(this.getDefaultChannelName())

    const releaseInfo = await this.getLatestVersionInfo(cancellationToken)
    const asset = releaseInfo.assets.find(it => it.name === channelFile)
    if (asset == null) {
      // html_url must be always, but just to be sure
      throw newError(`Cannot find ${channelFile} in the release ${releaseInfo.html_url || releaseInfo.name}`, "ERR_UPDATER_CHANNEL_FILE_NOT_FOUND")
    }

    const url = new URL(asset.url)
    let result: any
    try {
      result = safeLoad((await this.httpRequest(url, this.configureHeaders("application/octet-stream"), cancellationToken))!!)
    }
    catch (e) {
      if (e instanceof HttpError && e.statusCode === 404) {
        throw newError(`Cannot find ${channelFile} in the latest release artifacts (${url}): ${e.stack || e.message}`, "ERR_UPDATER_CHANNEL_FILE_NOT_FOUND")
      }
      throw e
    }

    (result as PrivateGitHubUpdateInfo).assets = releaseInfo.assets
    return result
  }
Example #8
Source File: Provider.ts    From electron-differential-updater with MIT License 6 votes vote down vote up
export function parseUpdateInfo(rawData: string | null, channelFile: string, channelFileUrl: URL): UpdateInfo {
  if (rawData == null) {
    throw newError(`Cannot parse update info from ${channelFile} in the latest release artifacts (${channelFileUrl}): rawData: null`, "ERR_UPDATER_INVALID_UPDATE_INFO")
  }

  let result: UpdateInfo
  try {
    result = safeLoad(rawData) as UpdateInfo
  }
  catch (e) {
    throw newError(`Cannot parse update info from ${channelFile} in the latest release artifacts (${channelFileUrl}): ${e.stack || e.message}, rawData: ${rawData}`, "ERR_UPDATER_INVALID_UPDATE_INFO")
  }
  return result
}
Example #9
Source File: MagicBrick.tsx    From next-basics with GNU General Public License v3.0 5 votes vote down vote up
export function MagicBrick(props: MagicBrickProps): React.ReactElement {
  const [useBrickConf, setUseBrickConf] = useState<UseSingleBrickConf>();

  useEffect(() => {
    async function fetchData(): Promise<void> {
      const magicBrickConfigMap = await getRuntime().getMagicBrickConfigMapAsync();
      if (magicBrickConfigMap.has(props.showType)) {
        const data = magicBrickConfigMap.get(props.showType);
        try {
          const $$parsedProperties = data.properties
            ? safeLoad(data.properties, { schema: JSON_SCHEMA, json: true })
            : {};
          const parsedTransform = data.transform
            ? safeLoad(data.transform, { schema: JSON_SCHEMA, json: true })
            : {};
          const parsedEvents = data.events
            ? safeLoad(data.events, { schema: JSON_SCHEMA, json: true })
            : {};
          const useBrickConf: UseSingleBrickConf = {
            brick: data.brick,
            properties: $$parsedProperties as Record<string, unknown>,
            transform: parsedTransform as GeneralTransform,
            events: parsedEvents as BrickEventsMap,
          };
          await developHelper.loadDynamicBricksInBrickConf(
            useBrickConf as BrickConf
          );
          setUseBrickConf(useBrickConf);
        } catch (e) {
          // eslint-disable-next-line no-console
          console.error(
            `请检查 ${props.showType} 的配置信息是否为有效的yaml数据`
          );
        }
      } else {
        // eslint-disable-next-line no-console
        console.error(`请检查是否存在 ${props.showType} 对应的配置信息`);
      }
    }
    if (props.showType) {
      fetchData();
    }
  }, [props.showType]);

  return (
    <>
      {useBrickConf && (
        <BrickAsComponent useBrick={useBrickConf} data={props.data} />
      )}
    </>
  );
}
Example #10
Source File: processor.ts    From next-basics with GNU General Public License v3.0 5 votes vote down vote up
export function yaml(value: string): any {
  return safeLoad(value, { schema: JSON_SCHEMA, json: true });
}
Example #11
Source File: buildStoryboardV2.ts    From next-basics with GNU General Public License v3.0 5 votes vote down vote up
/**
 * Refined building storyboard with graph api response.
 */
export function buildStoryboardV2(data: BuildInfoV2): StoryboardToBuild {
  const keepIds = data.options?.keepIds;
  const ctx: BuildContext = {
    keepIds,
  };

  const routes = buildRoutes(data.routeList, ctx);

  const customTemplates = data.templateList?.map((template) => ({
    name: template.templateId,
    proxy: template.proxy,
    state: template.state,
    bricks: buildBricks(template.children, ctx) as BrickConfInTemplate[],
    ...(keepIds
      ? {
          [symbolForNodeId]: template.id,
        }
      : undefined),
  }));

  const menus = data.menus?.map(normalizeMenu);

  const i18n = data.i18n?.reduce(
    (acc, node) => {
      acc.en[node.name] = node.en;
      acc.zh[node.name] = node.zh;
      return acc;
    },
    {
      en: {},
      zh: {},
    } as Record<string, Record<string, string>>
  );

  const functions = data.functions?.map((fn) => ({
    name: fn.name,
    source: fn.source,
    typescript: fn.typescript,
  }));

  const meta = {
    customTemplates,
    menus,
    i18n,
    functions,
    mocks: data.mocks,
  };

  // 基于当前最新的 storyboard 扫描 contract 信息
  const { contractData: contractStr } = ScanBricksAndTemplates({
    storyboard: {
      app: data.app,
      routes,
      meta,
    },
    version: "workspace",
    dependencies: data.dependencies,
  });

  const deps: DependContract[] = get(
    safeLoad(contractStr, { schema: JSON_SCHEMA, json: true }),
    "contracts[0].deps"
  );

  return {
    routes,
    meta: {
      ...meta,
      contracts: deps?.filter(
        (item) => item.type === "contract"
      ) as DependContractOfApi[],
    },
    dependsAll: data.dependsAll,
  };
}
Example #12
Source File: AppUpdater.ts    From electron-differential-updater with MIT License 5 votes vote down vote up
private async loadUpdateConfig(): Promise<any> {
    if (this._appUpdateConfigPath == null) {
      this._appUpdateConfigPath = this.app.appUpdateConfigPath;
    }
    return safeLoad(await readFile(this._appUpdateConfigPath, "utf-8"));
  }
Example #13
Source File: prepareAppZip.ts    From electron-differential-updater with MIT License 5 votes vote down vote up
(() => {
  APP_NAME = app.name;
  APP_VERSION = app.version;
  if (process.platform === "win32") {
    console.log(
      "This method only supports MAC, windows create default app installer on installation"
    );
    isZipCreatedForDiffDownload = true;
    return;
  } else if (process.platform === "darwin") {
    const data =  safeLoad(fs.readFileSync(app.appUpdateConfigPath, "utf-8"));
    const dirName = data?.updaterCacheDirName;
    const appCacheDirName = path.join(
      getAppCacheDir(),
      app.isPackaged ? (dirName || `${APP_NAME}-updater`) : "Electron"
    );

    const zipName = `${APP_NAME}-${APP_VERSION}-mac.zip`;

    const cacheCurrentFile = path.join(appCacheDirName, zipName);
    if (fs.existsSync(cacheCurrentFile)) {
      isZipCreatedForDiffDownload = true;

      return;
    }
    try {
      if (!fs.existsSync(appCacheDirName)) {
        fs.mkdirSync(appCacheDirName, { recursive: true });
      }

      let files = fs.readdirSync(appCacheDirName);
      for (const fileName of files) {
        if (fileName.endsWith(".zip") && fileName !== zipName) {
          fs.unlinkSync(path.join(appCacheDirName, fileName));
        }
      }
      let appZipPath = path.normalize(app.appPath + "/../../..");

      console.log("App zip file does not exist, Creting zip file in cache");
      let createZip = exec(
        `ditto -c -k --sequesterRsrc --keepParent "${appZipPath}" "${cacheCurrentFile}"`
      );
      createZip.stderr.on("close", (code: any) => {
        if (code) {
          console.error(
            "Error while creating zip for differential download",
            code
          );

          isZipCreatedForDiffDownload = true;
          throw new Error("Error while creating zip for differential download");
        } else {
          isZipCreatedForDiffDownload = true;
          console.log(
            "Successfully generated zip file for differential download"
          );
        }
      });
    } catch (e) {
      console.error(e);
      isZipCreatedForDiffDownload = true;
      throw new Error(e);
    }
  } else {
    console.log("no support for linux for differential update");
    isZipCreatedForDiffDownload = true;
    return;
  }
})();
Example #14
Source File: test.ts    From actions-builder-conversation-components-nodejs with Apache License 2.0 4 votes vote down vote up
// tslint:disable:only-arrow-functions

describe('My Action Test Suite', function() {
  // Set the timeout for each test run to 60s.
  this.timeout(60000);
  let test: ActionsOnGoogleTestManager;

  async function startConversation() {
    await test.sendQuery(TRIGGER_PHRASE);
    test.assertSpeech(CONTINUE_CONVO_PROMPT);
    test.assertText(CONTINUE_CONVO_PROMPT);
    test.assertIntent('actions.intent.MAIN');
    test.assertScene('Prompts');
  }

  // Load project ID and sample display name from project settings.
  function loadProjectSettings() {
    try {
      let fileContents = readFileSync(
        resolve(__dirname, '../sdk/settings/settings.yaml'), 'utf8');
      let data = safeLoad(fileContents) as any;
      PROJECT_ID = data.projectId;
      TRIGGER_PHRASE = `Talk to ${data.localizedSettings.displayName}`;
    } catch (e) {
        console.log(e);
    }
  }

  before('before all', async function() {
    // Load project settings to read project ID and trigger phrase.
    loadProjectSettings();
    test = new ActionsOnGoogleTestManager({ projectId: PROJECT_ID });
    await test.writePreviewFromDraft();
    test.setSuiteLocale(DEFAULT_LOCALE);
    test.setSuiteSurface(DEFAULT_SURFACE);
  });

  afterEach('post test cleans', async function() {
    test.cleanUpAfterTest();
  });

  it('trigger only', async function() {
    test.setTestSurface('SMART_DISPLAY');
    await startConversation();
    await test.sendStop();
    test.assertConversationEnded();
  });

  it('display simple', async function() {
    await startConversation();
    await test.sendQuery('Simple');
    const expectedExact =
        `This is the first simple response.This is the last simple response. ${
            CONTINUE_CONVO_PROMPT}`;
    const expectedRegex =
        `This is .* ${CONTINUE_CONVO_PROMPT}`;
    // Assert speech is exact match using `isExact` argument.
    test.assertSpeech(expectedExact, {isExact: true});
    // Assert text with regex using `isRegex` argument.
    test.assertText(expectedRegex, {isRegexp: true});
    test.assertIntent('simple');
    await test.sendStop();
    test.assertConversationEnded();
  });

  it('display image', async function() {
    await startConversation();
    await test.sendQuery('Image');
    const expected = `This is an image prompt!${CONTINUE_CONVO_PROMPT}`;
    test.assertSpeech(expected);
    test.assertText(expected);
    test.assertIntent('image');
    test.assertImage({
      url: 'https://developers.google.com/assistant/assistant_96.png',
      alt: 'Google Assistant logo'
    });
    await test.sendStop();
    test.assertConversationEnded();
  });

  it('display card', async function() {
    await startConversation();
    await test.sendQuery('Card');
    const expected = `This is a card.${CONTINUE_CONVO_PROMPT}`;
    test.assertSpeech(expected);
    test.assertText(expected);
    test.assertIntent('card');
    test.assertCard({
      title: 'Card Title',
      subtitle: 'Card Subtitle',
      text: 'Card Content',
      image: {
        url: 'https://developers.google.com/assistant/assistant_96.png',
        alt: 'Google Assistant logo',
        height: 0,
        width: 0
      }
    });
    await test.sendStop();
    test.assertConversationEnded();
  });

  it('display table', async function() {
    await startConversation();
    await test.sendQuery('Table');
    const expected = `This is a table.${CONTINUE_CONVO_PROMPT}`;
    test.assertSpeech(expected);
    test.assertText(expected);
    test.assertIntent('table');

    test.assertTable({
      title: 'Table Title',
      subtitle: 'Table Subtitle',
      image: {
        url: 'https://developers.google.com/assistant/assistant_96.png',
        alt: 'Google Assistant logo',
        height: 0,
        width: 0
      },
      columns: [
        {align: 'UNSPECIFIED', header: 'Column A'},
        {align: 'UNSPECIFIED', header: 'Column B'},
        {align: 'UNSPECIFIED', header: 'Column C'}
      ],
      rows: [
        {cells: [{text: 'A1'}, {text: 'B1'}, {text: 'C1'}], divider: false},
        {cells: [{text: 'A2'}, {text: 'B2'}, {text: 'C2'}], divider: false},
        {cells: [{text: 'A3'}, {text: 'B3'}, {text: 'C3'}], divider: false}
      ],
    });
    await test.sendStop();
    test.assertConversationEnded();
  });

  it('display list', async function() {
    await startConversation();
    await test.sendQuery('List');
    const expected = `This is a list.`;
    test.assertSpeech(expected);
    test.assertText(expected);
    test.assertIntent('list');
    test.assertList({
      title: 'List title',
      subtitle: 'List subtitle',
      items:
          [{key: 'ITEM_1'}, {key: 'ITEM_2'}, {key: 'ITEM_3'}, {key: 'ITEM_4'}]
    });
    await test.sendStop();
    test.assertConversationEnded();
  });

  it('display collection', async function() {
    await startConversation();
    await test.sendQuery('Collection');
    const expected = `This is a collection.`;
    test.assertSpeech(expected);
    test.assertText(expected);
    test.assertIntent('collection');
    test.assertCollection({
      title: 'Collection Title',
      subtitle: 'Collection subtitle',
      items:
          [{key: 'ITEM_1'}, {key: 'ITEM_2'}, {key: 'ITEM_3'}, {key: 'ITEM_4'}]
    });
    await test.sendStop();
    test.assertConversationEnded();
  });

  it('display media', async function() {
    await startConversation();
    await test.sendQuery('Media');
    const expected = `This is a media response${CONTINUE_CONVO_PROMPT}`;
    test.assertSpeech(expected);
    test.assertText(expected);
    test.assertIntent('media');
    test.assertMedia({
          optionalMediaControls: ['PAUSED', 'STOPPED'] as any,
          mediaObjects: [{
            name: 'Media name',
            description: 'Media description',
            url:
                'https://actions.google.com/sounds/v1/cartoon/cartoon_boing.ogg',
            image: {
              image: 'large',
              large: {
                url: 'https://developers.google.com/assistant/assistant_96.png',
                alt: 'Google Assistant logo',
                height: 0,
                width: 0
              }
            } as any
          }],
          mediaType: 'AUDIO'
        });
    await test.sendStop();
    test.assertConversationEnded();
  });

  it('select list item', async function() {
    await startConversation();
    await test.sendQuery('List');
    let expected = `This is a list.`;
    test.assertSpeech(expected);
    test.assertText(expected);
    test.assertIntent('list');
    test.assertConversationNotEnded();

    await test.sendQuery('Item #1');
    expected = `You selected item #1.${CONTINUE_CONVO_PROMPT}`;
    test.assertSpeech(expected);
    test.assertText(expected);
    test.assertScene('Prompts');
    await test.sendStop();
    test.assertConversationEnded();
  });

  it('select collection item', async function() {
    await startConversation();
    await test.sendQuery('Collection');
    let expected = `This is a collection.`;
    test.assertSpeech(expected);
    test.assertText(expected);
    test.assertIntent('collection');
    test.assertConversationNotEnded();

    await test.sendQuery('Item #1');
    expected = `You selected item #1.${CONTINUE_CONVO_PROMPT}`;
    test.assertSpeech(expected);
    test.assertText(expected);
    test.assertScene('Prompts');
    await test.sendStop();
    test.assertConversationEnded();
  });
});
Example #15
Source File: aslYamlLanguageService.ts    From amazon-states-language-service with MIT License 4 votes vote down vote up
getLanguageService = function(params: LanguageServiceParams, schema: JSONSchema, aslLanguageService: LanguageService): LanguageService {
    const builtInParams = {}

    const languageService = getLanguageServiceVscode({
        ...params,
        ...builtInParams,
    })

    const requestServiceMock = async function(uri: string): Promise<string> {
        return new Promise<string>(c => {
            c(JSON.stringify(schema))
        })
    }
    const schemaService = new YAMLSchemaService(requestServiceMock, params.workspaceContext)
    // initialize schema
    schemaService.registerExternalSchema(LANGUAGE_IDS.YAML, ['*'], schema)
    schemaService.getOrAddSchemaHandle(LANGUAGE_IDS.YAML, schema)

    const completer = new YAMLCompletion(schemaService)

    languageService.doValidation = async function(
        textDocument: TextDocument
    ) {
        const yamlDocument: YAMLDocument = parseYAML(textDocument.getText())
        const validationResult: Diagnostic[] = []

        for (const currentYAMLDoc of yamlDocument.documents) {
            const validation = await aslLanguageService.doValidation(textDocument, currentYAMLDoc)
            validationResult.push(
                ...currentYAMLDoc.errors
                    .concat(currentYAMLDoc.warnings)
                    .map(err => convertYAMLDiagnostic(err, textDocument))
            )
            validationResult.push(...validation)
        }

        return validationResult
    }

    languageService.doComplete = async function(
        document: TextDocument,
        position: Position
    ): Promise<CompletionList> {
        const {
            modifiedDocText,
            tempPositionForCompletions,
            startPositionForInsertion,
            endPositionForInsertion,
            shouldPrependSpace
        } = processYamlDocForCompletion(document, position)

        const processedDocument = TextDocument.create(document.uri, document.languageId, document.version, modifiedDocText)

        const offsetIntoOriginalDocument = document.offsetAt(position)
        const offsetIntoProcessedDocument = processedDocument.offsetAt(tempPositionForCompletions)

        const processedYamlDoc: YAMLDocument = parseYAML(modifiedDocText)
        const currentDoc = matchOffsetToDocument(offsetIntoProcessedDocument, processedYamlDoc)

        if (!currentDoc) {
            return { items: [], isIncomplete: false }
        }

        const positionForDoComplete = {...tempPositionForCompletions} // Copy position to new object since doComplete modifies the position
        const yamlCompletions = await completer.doComplete(processedDocument, positionForDoComplete, false)
        // yaml-language-server does not output correct completions for retry/catch
        // we need to overwrite the text
        function updateCompletionText(item: CompletionItem, text: string) {
            item.insertText = text

            if (item.textEdit) {
                item.textEdit.newText = text
            }
        }

        yamlCompletions.items.forEach(item => {
            if (item.label === 'Catch') {
                updateCompletionText(item, CATCH_INSERT)
            } else if (item.label === 'Retry') {
                updateCompletionText(item, RETRY_INSERT)
            }
        })

        const { isDirectChildOfStates, isWithinCatchRetryState, hasCatchPropSibling, hasRetryPropSibling } = getOffsetData(document, offsetIntoOriginalDocument)

        const aslOptions: ASLOptions = {
            ignoreColonOffset: true,
            shouldShowStateSnippets: isDirectChildOfStates,
            shouldShowErrorSnippets: {
                retry: isWithinCatchRetryState && !hasRetryPropSibling,
                catch: isWithinCatchRetryState && !hasCatchPropSibling
            }
        }

        const aslCompletions: CompletionList  = doCompleteAsl(processedDocument, tempPositionForCompletions, currentDoc, yamlCompletions, aslOptions)

        const modifiedAslCompletionItems: CompletionItem[] = aslCompletions.items.map(completionItem => {
            const completionItemCopy = {...completionItem} // Copy completion to new object to avoid overwriting any snippets

            if (completionItemCopy.insertText && completionItemCopy.kind === CompletionItemKind.Snippet && document.languageId === LANGUAGE_IDS.YAML) {
                completionItemCopy.insertText = convertJsonSnippetToYaml(completionItemCopy.insertText)
            } else {
                const currentTextEdit = completionItemCopy.textEdit

                if (currentTextEdit) {
                    if (shouldPrependSpace) {
                        if (currentTextEdit.newText && currentTextEdit.newText.charAt(0) !== ' ') {
                            currentTextEdit.newText = ' ' + currentTextEdit.newText
                        }
                        if (completionItemCopy.insertText && completionItemCopy.insertText.charAt(0) !== ' ') {
                            completionItemCopy.insertText = ' ' + completionItemCopy.insertText
                        }
                    }

                    currentTextEdit.range.start = startPositionForInsertion
                    currentTextEdit.range.end = endPositionForInsertion

                    // Completions that include both a key and a value should replace everything right of the cursor.
                    if (completionItemCopy.kind === CompletionItemKind.Property) {
                        currentTextEdit.range.end = {
                            line: endPositionForInsertion.line,
                            character: document.getText().length
                        }
                    }
                }
            }

            return completionItemCopy
        })

        const modifiedAslCompletions: CompletionList = {
            isIncomplete: aslCompletions.isIncomplete,
            items: modifiedAslCompletionItems
        }

        return Promise.resolve(modifiedAslCompletions)
    }

    languageService.doHover = function(
        document: TextDocument,
        position: Position
    ): Thenable<Hover | null> {
        const doc = parseYAML(document.getText())
        const offset = document.offsetAt(position)
        const currentDoc = matchOffsetToDocument(offset, doc)
        if (!currentDoc) {
            // tslint:disable-next-line: no-null-keyword
            return Promise.resolve(null)
        }

        const currentDocIndex = doc.documents.indexOf(currentDoc)
        currentDoc.currentDocIndex = currentDocIndex

        return aslLanguageService.doHover(document, position, currentDoc)
    }

    languageService.format = function(
        document: TextDocument,
        range: Range,
        options: FormattingOptions
    ): TextEdit[] {
        try {
            const text = document.getText()
            const formatted = safeDump(safeLoad(text), { indent: options.tabSize })

            return [TextEdit.replace(Range.create(Position.create(0, 0), document.positionAt(text.length)), formatted)]
        } catch (error) {
            return []
        }
    }

    languageService.findDocumentSymbols = function(document: TextDocument): SymbolInformation[] {
        const doc = parseYAML(document.getText())
        if (!doc || doc.documents.length === 0) {
            return []
        }

        let results: any[] = []
        for (const yamlDoc of doc.documents) {
            if (yamlDoc.root) {
                results = results.concat(aslLanguageService.findDocumentSymbols(document, yamlDoc))
            }
        }

        return results
    }

    languageService.findDocumentSymbols2 = function(document: TextDocument): DocumentSymbol[] {
        const doc = parseYAML(document.getText())
        if (!doc || doc.documents.length === 0) {
            return []
        }

        let results: any[] = []
        for (const yamlDoc of doc.documents) {
            if (yamlDoc.root) {
                results = results.concat(aslLanguageService.findDocumentSymbols2(document, yamlDoc))
            }
        }

        return results
    }

    return languageService
}
Example #16
Source File: index.ts    From metasearch with Apache License 2.0 4 votes vote down vote up
(async () => {
  // Set up exception handler
  const exceptionHandler = (ex: any) => {
    console.error(`\x1b[31m${ex.message}\x1b[0m`);
    if (ex.isAxiosError) {
      const {
        request: { method, path },
        response: { data, headers, status },
      } = ex;
      console.error(`${status} ${method} ${path}:
${JSON.stringify(headers)}
${JSON.stringify(data)}`);
    }
    process.exit(1);
  };
  process.on("uncaughtException", exceptionHandler);
  process.on("unhandledRejection", exceptionHandler);

  // Load config
  interface Config {
    engines: Record<string, { name?: string }>;
    footer?: string;
    trackingId?: string;
  }
  const config: Config = (() => {
    const DOCKER_MOUNT = "/data";
    const CONFIG_FILENAME = "config.yaml";

    // Locate user-provided config file
    const dockerizedConfig = `${DOCKER_MOUNT}/${CONFIG_FILENAME}`;
    const configFile = fs.existsSync("/.dockerenv")
      ? dockerizedConfig
      : CONFIG_FILENAME;
    if (!fs.existsSync(configFile)) {
      throw Error(`Metasearch config file '${configFile}' not found`);
    }

    // Parse user-provided config file and expand environment variables
    const userConfig: Config = safeLoad(
      fs
        .readFileSync(configFile, "utf8")
        .replace(/\$\{(\w+)\}/g, ({}, varName) => {
          const varValue = process.env[varName];
          if (varValue) {
            return varValue;
          }

          // Keep ${FOOBAR} because it's used as an example in the YAML comment
          if (varName === "FOOBAR") {
            return "${FOOBAR}";
          }

          throw Error(
            `Config references nonexistent environment variable '${varName}'`,
          );
        }),
    );

    /** Recursively pulls out all values from a complex object */
    const allValues = (node: any): Set<any> =>
      new Set(
        node && typeof node === "object"
          ? (Array.isArray(node) ? node : Object.values(node)).flatMap(child =>
              Array.from(allValues(child)),
            )
          : [node],
      );

    // Abort if user didn't follow instructions to customize config.yaml
    if (allValues(userConfig).has("example")) {
      throw Error(
        "The engine options in config.yaml are populated with dummy values. Please customize the option values for engines you want to use and delete the config blocks for engines you don't want to use.",
      );
    }

    return userConfig;
  })();
  if (!config.engines) {
    throw Error("No engines specified");
  }

  // Initialize engines
  const uninitializedEngineMap = Object.fromEntries(
    engines.map(e => [e.id, e]),
  );
  const engineMap = Object.fromEntries(
    Object.entries(config.engines).map(([id, options]) => {
      const uninitializedEngine = uninitializedEngineMap[id];
      if (!uninitializedEngine) {
        throw Error(`Unrecognized engine '${id}'`);
      }
      uninitializedEngine.init(options);
      return [
        id,
        {
          ...uninitializedEngine,
          name: options.name ?? uninitializedEngine.name,
        },
      ];
    }),
  );

  // Generate index.html
  fs.writeFileSync(
    "dist/index.html",
    await ejs.renderFile("src/ui/index.html", {
      metasearch: {
        ENGINES: engineMap,
        FOOTER: config.footer,
        TRACKING_ID: config.trackingId,
      },
    }),
    "utf8",
  );

  // Set up server
  const app = express();
  const port = process.env.PORT || 3000;
  app.use(compression());
  app.use(express.static("dist"));

  // Declare search route for individual engines
  app.get("/api/search", async (req, res) => {
    // Check that desired engine exists
    const { engine: engineId, q } = req.query as Record<string, string>;
    const engine = engineMap[engineId];
    if (!engine) {
      res.status(400);
      res.json({ error: `Unknown engine: ${engineId}` });
      return;
    }

    // Query engine
    try {
      res.json(
        (await engine.search(q)).map(result => ({
          ...result,
          snippet: result.snippet
            ? sanitizeHtml(
                engine.isSnippetLarge
                  ? `<blockquote>${result.snippet}</blockquote>`
                  : result.snippet,
              )
            : undefined,
        })),
      );
    } catch (ex) {
      // TODO: Instead return 500 and show error UI
      res.json([]);

      // If Axios error, keep only the useful parts
      if (ex.isAxiosError) {
        const {
          code,
          config: { baseURL, method, url },
          response: { data = undefined, status = undefined } = {},
        } = ex as AxiosError;
        console.error(
          `${
            status ?? code
          } ${method?.toUpperCase()} ${baseURL}${url}: ${JSON.stringify(data)}`,
        );
      } else {
        console.error(ex);
      }
    }
  });

  // Start server
  const httpTerminator = createHttpTerminator({
    server: app.listen(port, () => {
      console.log(`Serving Metasearch at http://localhost:${port}`);
    }),
  });
  ["SIGINT", "SIGTERM"].forEach(signal =>
    process.on(signal, async () => {
      console.log("Gracefully shutting down...");
      await httpTerminator.terminate();
      console.log("Closed all open connections. Bye!");
      process.exit(0);
    }),
  );
})();