Showing preview only (747K chars total). Download the full file or copy to clipboard to get everything.
Repository: guess-js/guess
Branch: master
Commit: 0def89f41d43
Files: 452
Total size: 630.1 KB
Directory structure:
gitextract_rzzsvapf/
├── .gitignore
├── .travis.yml
├── CONTRIBUTING.md
├── DEVELOPING.md
├── LICENSE
├── README.md
├── experiments/
│ └── guess-static-sites/
│ ├── .gitignore
│ ├── README.md
│ ├── config.js
│ ├── generatePredictions.js
│ ├── package.json
│ ├── predictiveFetching.js
│ ├── server.js
│ ├── src/
│ │ ├── models/
│ │ │ ├── pageView.js
│ │ │ └── prediction.js
│ │ ├── parser.js
│ │ └── queryParams.js
│ └── test/
│ ├── clientTests.js
│ ├── fixtures/
│ │ ├── gaResponse.json
│ │ ├── server.js
│ │ └── test.html
│ ├── gaClientTests.js
│ └── serverTests.js
├── infra/
│ ├── e2e.ts
│ ├── install.ts
│ ├── pretest.ts
│ └── test.ts
├── jest-puppeteer.config.js
├── jest.config.js
├── lerna.json
├── package.json
├── packages/
│ ├── common/
│ │ ├── interfaces.ts
│ │ └── logger.ts
│ ├── guess-ga/
│ │ ├── .npmignore
│ │ ├── README.md
│ │ ├── index.ts
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── client.ts
│ │ │ ├── ga.ts
│ │ │ └── normalize.ts
│ │ ├── test/
│ │ │ └── normalize.spec.ts
│ │ ├── tsconfig.json
│ │ └── webpack.config.js
│ ├── guess-parser/
│ │ ├── .npmignore
│ │ ├── README.md
│ │ ├── index.ts
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── angular/
│ │ │ │ ├── index.ts
│ │ │ │ ├── modules.ts
│ │ │ │ ├── route-parser.ts
│ │ │ │ └── routes.ts
│ │ │ ├── detector/
│ │ │ │ ├── detect.ts
│ │ │ │ └── index.ts
│ │ │ ├── language-service.ts
│ │ │ ├── parser.ts
│ │ │ ├── preact/
│ │ │ │ └── index.ts
│ │ │ ├── react/
│ │ │ │ ├── base.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── react-jsx.ts
│ │ │ │ └── react-tsx.ts
│ │ │ └── utils.ts
│ │ ├── test/
│ │ │ ├── angular.spec.ts
│ │ │ ├── detect.spec.ts
│ │ │ ├── fixtures/
│ │ │ │ ├── angular/
│ │ │ │ │ ├── .angular-cli.json
│ │ │ │ │ ├── library/
│ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ ├── library.module.ts
│ │ │ │ │ │ ├── nested/
│ │ │ │ │ │ │ └── library.module.ts
│ │ │ │ │ │ └── tsconfig.json
│ │ │ │ │ ├── package.json
│ │ │ │ │ ├── src/
│ │ │ │ │ │ ├── app/
│ │ │ │ │ │ │ ├── about/
│ │ │ │ │ │ │ │ ├── about-routing.module.ts
│ │ │ │ │ │ │ │ └── about.module.ts
│ │ │ │ │ │ │ ├── app-routing.module.ts
│ │ │ │ │ │ │ ├── app.component.css
│ │ │ │ │ │ │ ├── app.component.html
│ │ │ │ │ │ │ ├── app.component.spec.ts
│ │ │ │ │ │ │ ├── app.component.ts
│ │ │ │ │ │ │ ├── app.module.ts
│ │ │ │ │ │ │ ├── bar/
│ │ │ │ │ │ │ │ ├── bar.module.ts
│ │ │ │ │ │ │ │ └── baz/
│ │ │ │ │ │ │ │ ├── baz.module.ts
│ │ │ │ │ │ │ │ └── cycle-parent.ts
│ │ │ │ │ │ │ ├── bar-simple.component.ts
│ │ │ │ │ │ │ ├── foo/
│ │ │ │ │ │ │ │ ├── baz/
│ │ │ │ │ │ │ │ │ ├── baz-routing.module.ts
│ │ │ │ │ │ │ │ │ ├── baz.component.ts
│ │ │ │ │ │ │ │ │ └── baz.module.ts
│ │ │ │ │ │ │ │ ├── foo-routing.module.ts
│ │ │ │ │ │ │ │ ├── foo.component.ts
│ │ │ │ │ │ │ │ └── foo.module.ts
│ │ │ │ │ │ │ ├── lazy/
│ │ │ │ │ │ │ │ ├── lazy-routing.module.ts
│ │ │ │ │ │ │ │ ├── lazy.component.ts
│ │ │ │ │ │ │ │ └── lazy.module.ts
│ │ │ │ │ │ │ ├── qux/
│ │ │ │ │ │ │ │ └── qux.module.ts
│ │ │ │ │ │ │ └── wrapper/
│ │ │ │ │ │ │ └── wrapper.module.ts
│ │ │ │ │ │ ├── environments/
│ │ │ │ │ │ │ ├── environment.prod.ts
│ │ │ │ │ │ │ └── environment.ts
│ │ │ │ │ │ ├── index.html
│ │ │ │ │ │ ├── main.ts
│ │ │ │ │ │ ├── polyfills.ts
│ │ │ │ │ │ ├── styles.css
│ │ │ │ │ │ ├── test.ts
│ │ │ │ │ │ ├── tsconfig.app.json
│ │ │ │ │ │ └── tsconfig.spec.json
│ │ │ │ │ └── tsconfig.json
│ │ │ │ ├── gatsby/
│ │ │ │ │ ├── .gitignore
│ │ │ │ │ ├── .prettierrc
│ │ │ │ │ ├── LICENSE
│ │ │ │ │ ├── README.md
│ │ │ │ │ ├── gatsby-browser.js
│ │ │ │ │ ├── gatsby-config.js
│ │ │ │ │ ├── gatsby-node.js
│ │ │ │ │ ├── gatsby-ssr.js
│ │ │ │ │ ├── package.json
│ │ │ │ │ └── src/
│ │ │ │ │ ├── components/
│ │ │ │ │ │ └── header.js
│ │ │ │ │ ├── layouts/
│ │ │ │ │ │ ├── index.css
│ │ │ │ │ │ └── index.js
│ │ │ │ │ └── pages/
│ │ │ │ │ ├── 404.js
│ │ │ │ │ ├── index.js
│ │ │ │ │ └── page-2.js
│ │ │ │ ├── ng8/
│ │ │ │ │ ├── .editorconfig
│ │ │ │ │ ├── .gitignore
│ │ │ │ │ ├── README.md
│ │ │ │ │ ├── angular.json
│ │ │ │ │ ├── browserslist
│ │ │ │ │ ├── e2e/
│ │ │ │ │ │ ├── protractor.conf.js
│ │ │ │ │ │ ├── src/
│ │ │ │ │ │ │ ├── app.e2e-spec.ts
│ │ │ │ │ │ │ └── app.po.ts
│ │ │ │ │ │ └── tsconfig.json
│ │ │ │ │ ├── karma.conf.js
│ │ │ │ │ ├── package.json
│ │ │ │ │ ├── src/
│ │ │ │ │ │ ├── app/
│ │ │ │ │ │ │ ├── app-routing.module.ts
│ │ │ │ │ │ │ ├── app.component.css
│ │ │ │ │ │ │ ├── app.component.html
│ │ │ │ │ │ │ ├── app.component.spec.ts
│ │ │ │ │ │ │ ├── app.component.ts
│ │ │ │ │ │ │ └── app.module.ts
│ │ │ │ │ │ ├── assets/
│ │ │ │ │ │ │ └── .gitkeep
│ │ │ │ │ │ ├── environments/
│ │ │ │ │ │ │ ├── environment.prod.ts
│ │ │ │ │ │ │ └── environment.ts
│ │ │ │ │ │ ├── index.html
│ │ │ │ │ │ ├── main.ts
│ │ │ │ │ │ ├── polyfills.ts
│ │ │ │ │ │ ├── styles.css
│ │ │ │ │ │ └── test.ts
│ │ │ │ │ ├── tsconfig.app.json
│ │ │ │ │ ├── tsconfig.json
│ │ │ │ │ ├── tsconfig.spec.json
│ │ │ │ │ └── tslint.json
│ │ │ │ ├── nx/
│ │ │ │ │ ├── LICENSE
│ │ │ │ │ ├── README.md
│ │ │ │ │ ├── angular.json
│ │ │ │ │ ├── apps/
│ │ │ │ │ │ ├── customers-ui-e2e/
│ │ │ │ │ │ │ └── src/
│ │ │ │ │ │ │ └── integration/
│ │ │ │ │ │ │ └── customers.component.spec.ts
│ │ │ │ │ │ ├── feat-home-e2e/
│ │ │ │ │ │ │ ├── cypress.json
│ │ │ │ │ │ │ ├── src/
│ │ │ │ │ │ │ │ ├── fixtures/
│ │ │ │ │ │ │ │ │ └── example.json
│ │ │ │ │ │ │ │ ├── integration/
│ │ │ │ │ │ │ │ │ └── home.component.spec.ts
│ │ │ │ │ │ │ │ ├── plugins/
│ │ │ │ │ │ │ │ │ └── index.js
│ │ │ │ │ │ │ │ └── support/
│ │ │ │ │ │ │ │ ├── commands.ts
│ │ │ │ │ │ │ │ └── index.ts
│ │ │ │ │ │ │ ├── tsconfig.e2e.json
│ │ │ │ │ │ │ ├── tsconfig.json
│ │ │ │ │ │ │ └── tslint.json
│ │ │ │ │ │ ├── ng-cli-app/
│ │ │ │ │ │ │ ├── browserslist
│ │ │ │ │ │ │ ├── karma.conf.js
│ │ │ │ │ │ │ ├── src/
│ │ │ │ │ │ │ │ ├── app/
│ │ │ │ │ │ │ │ │ ├── app.component.html
│ │ │ │ │ │ │ │ │ ├── app.component.scss
│ │ │ │ │ │ │ │ │ ├── app.component.spec.ts
│ │ │ │ │ │ │ │ │ ├── app.component.ts
│ │ │ │ │ │ │ │ │ └── app.module.ts
│ │ │ │ │ │ │ │ ├── assets/
│ │ │ │ │ │ │ │ │ ├── .gitkeep
│ │ │ │ │ │ │ │ │ └── customers.json
│ │ │ │ │ │ │ │ ├── environments/
│ │ │ │ │ │ │ │ │ ├── environment.prod.ts
│ │ │ │ │ │ │ │ │ └── environment.ts
│ │ │ │ │ │ │ │ ├── index.html
│ │ │ │ │ │ │ │ ├── main.ts
│ │ │ │ │ │ │ │ ├── polyfills.ts
│ │ │ │ │ │ │ │ ├── styles.scss
│ │ │ │ │ │ │ │ └── test.ts
│ │ │ │ │ │ │ ├── tsconfig.app.json
│ │ │ │ │ │ │ └── tsconfig.spec.json
│ │ │ │ │ │ ├── ng-cli-app-e2e/
│ │ │ │ │ │ │ ├── cypress.json
│ │ │ │ │ │ │ ├── src/
│ │ │ │ │ │ │ │ ├── fixtures/
│ │ │ │ │ │ │ │ │ └── example.json
│ │ │ │ │ │ │ │ ├── integration/
│ │ │ │ │ │ │ │ │ ├── app.spec.ts
│ │ │ │ │ │ │ │ │ ├── customers.spec.ts
│ │ │ │ │ │ │ │ │ └── home.spec.ts
│ │ │ │ │ │ │ │ ├── plugins/
│ │ │ │ │ │ │ │ │ └── index.js
│ │ │ │ │ │ │ │ └── support/
│ │ │ │ │ │ │ │ ├── app.po.ts
│ │ │ │ │ │ │ │ ├── commands.ts
│ │ │ │ │ │ │ │ └── index.ts
│ │ │ │ │ │ │ ├── tsconfig.e2e.json
│ │ │ │ │ │ │ └── tsconfig.json
│ │ │ │ │ │ └── shared-components-e2e/
│ │ │ │ │ │ ├── cypress.json
│ │ │ │ │ │ ├── src/
│ │ │ │ │ │ │ ├── fixtures/
│ │ │ │ │ │ │ │ └── example.json
│ │ │ │ │ │ │ ├── integration/
│ │ │ │ │ │ │ │ ├── info-box/
│ │ │ │ │ │ │ │ │ └── info-box.component.spec.ts
│ │ │ │ │ │ │ │ └── navigation/
│ │ │ │ │ │ │ │ └── navigation.component.spec.ts
│ │ │ │ │ │ │ ├── plugins/
│ │ │ │ │ │ │ │ └── index.js
│ │ │ │ │ │ │ └── support/
│ │ │ │ │ │ │ ├── commands.ts
│ │ │ │ │ │ │ └── index.ts
│ │ │ │ │ │ ├── tsconfig.e2e.json
│ │ │ │ │ │ ├── tsconfig.json
│ │ │ │ │ │ └── tslint.json
│ │ │ │ │ ├── docs/
│ │ │ │ │ │ ├── _config.yml
│ │ │ │ │ │ └── index.md
│ │ │ │ │ ├── index.js
│ │ │ │ │ ├── jest.config.js
│ │ │ │ │ ├── karma.conf.js
│ │ │ │ │ ├── libs/
│ │ │ │ │ │ ├── .gitkeep
│ │ │ │ │ │ ├── auth/
│ │ │ │ │ │ │ ├── README.md
│ │ │ │ │ │ │ ├── jest.config.js
│ │ │ │ │ │ │ ├── src/
│ │ │ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ │ │ ├── lib/
│ │ │ │ │ │ │ │ │ ├── auth-routing.module.ts
│ │ │ │ │ │ │ │ │ ├── auth.guard.spec.ts
│ │ │ │ │ │ │ │ │ ├── auth.guard.ts
│ │ │ │ │ │ │ │ │ ├── auth.module.spec.ts
│ │ │ │ │ │ │ │ │ ├── auth.module.ts
│ │ │ │ │ │ │ │ │ ├── auth.service.spec.ts
│ │ │ │ │ │ │ │ │ ├── auth.service.ts
│ │ │ │ │ │ │ │ │ └── login/
│ │ │ │ │ │ │ │ │ ├── login.component.html
│ │ │ │ │ │ │ │ │ ├── login.component.scss
│ │ │ │ │ │ │ │ │ ├── login.component.spec.ts
│ │ │ │ │ │ │ │ │ └── login.component.ts
│ │ │ │ │ │ │ │ └── test-setup.ts
│ │ │ │ │ │ │ ├── tsconfig.json
│ │ │ │ │ │ │ ├── tsconfig.lib.json
│ │ │ │ │ │ │ ├── tsconfig.spec.json
│ │ │ │ │ │ │ └── tslint.json
│ │ │ │ │ │ ├── customers/
│ │ │ │ │ │ │ ├── data/
│ │ │ │ │ │ │ │ ├── README.md
│ │ │ │ │ │ │ │ ├── jest.config.js
│ │ │ │ │ │ │ │ ├── src/
│ │ │ │ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ │ │ │ ├── lib/
│ │ │ │ │ │ │ │ │ │ ├── customer.model.ts
│ │ │ │ │ │ │ │ │ │ ├── customer.service.spec.ts
│ │ │ │ │ │ │ │ │ │ └── customer.service.ts
│ │ │ │ │ │ │ │ │ └── test-setup.ts
│ │ │ │ │ │ │ │ ├── tsconfig.json
│ │ │ │ │ │ │ │ ├── tsconfig.lib.json
│ │ │ │ │ │ │ │ ├── tsconfig.spec.json
│ │ │ │ │ │ │ │ └── tslint.json
│ │ │ │ │ │ │ └── ui/
│ │ │ │ │ │ │ ├── README.md
│ │ │ │ │ │ │ ├── jest.config.js
│ │ │ │ │ │ │ ├── src/
│ │ │ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ │ │ ├── lib/
│ │ │ │ │ │ │ │ │ ├── customer-list/
│ │ │ │ │ │ │ │ │ │ ├── customer-list-datasource.ts
│ │ │ │ │ │ │ │ │ │ ├── customer-list.component.html
│ │ │ │ │ │ │ │ │ │ ├── customer-list.component.scss
│ │ │ │ │ │ │ │ │ │ ├── customer-list.component.spec.ts
│ │ │ │ │ │ │ │ │ │ └── customer-list.component.ts
│ │ │ │ │ │ │ │ │ ├── customers-routing.module.ts
│ │ │ │ │ │ │ │ │ ├── customers-ui.module.spec.ts
│ │ │ │ │ │ │ │ │ ├── customers-ui.module.ts
│ │ │ │ │ │ │ │ │ ├── customers.component.html
│ │ │ │ │ │ │ │ │ ├── customers.component.scss
│ │ │ │ │ │ │ │ │ ├── customers.component.spec.ts
│ │ │ │ │ │ │ │ │ └── customers.component.ts
│ │ │ │ │ │ │ │ └── test-setup.ts
│ │ │ │ │ │ │ ├── tsconfig.json
│ │ │ │ │ │ │ ├── tsconfig.lib.json
│ │ │ │ │ │ │ ├── tsconfig.spec.json
│ │ │ │ │ │ │ └── tslint.json
│ │ │ │ │ │ ├── home/
│ │ │ │ │ │ │ └── ui/
│ │ │ │ │ │ │ ├── .storybook/
│ │ │ │ │ │ │ │ ├── addons.js
│ │ │ │ │ │ │ │ ├── config.js
│ │ │ │ │ │ │ │ ├── tsconfig.json
│ │ │ │ │ │ │ │ └── webpack.config.js
│ │ │ │ │ │ │ ├── README.md
│ │ │ │ │ │ │ ├── jest.config.js
│ │ │ │ │ │ │ ├── src/
│ │ │ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ │ │ ├── lib/
│ │ │ │ │ │ │ │ │ ├── home-ui.module.spec.ts
│ │ │ │ │ │ │ │ │ ├── home-ui.module.ts
│ │ │ │ │ │ │ │ │ ├── home.component.html
│ │ │ │ │ │ │ │ │ ├── home.component.scss
│ │ │ │ │ │ │ │ │ ├── home.component.spec.ts
│ │ │ │ │ │ │ │ │ ├── home.component.stories.ts
│ │ │ │ │ │ │ │ │ └── home.component.ts
│ │ │ │ │ │ │ │ └── test-setup.ts
│ │ │ │ │ │ │ ├── tsconfig.json
│ │ │ │ │ │ │ ├── tsconfig.lib.json
│ │ │ │ │ │ │ ├── tsconfig.spec.json
│ │ │ │ │ │ │ └── tslint.json
│ │ │ │ │ │ └── shared/
│ │ │ │ │ │ └── components/
│ │ │ │ │ │ ├── .storybook/
│ │ │ │ │ │ │ ├── addons.js
│ │ │ │ │ │ │ ├── config.js
│ │ │ │ │ │ │ ├── preview-head.html
│ │ │ │ │ │ │ ├── tsconfig.json
│ │ │ │ │ │ │ └── webpack.config.js
│ │ │ │ │ │ ├── README.md
│ │ │ │ │ │ ├── jest.config.js
│ │ │ │ │ │ ├── src/
│ │ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ │ ├── lib/
│ │ │ │ │ │ │ │ ├── info-box/
│ │ │ │ │ │ │ │ │ ├── info-box.component.html
│ │ │ │ │ │ │ │ │ ├── info-box.component.scss
│ │ │ │ │ │ │ │ │ ├── info-box.component.spec.ts
│ │ │ │ │ │ │ │ │ ├── info-box.component.stories.ts
│ │ │ │ │ │ │ │ │ └── info-box.component.ts
│ │ │ │ │ │ │ │ ├── navigation/
│ │ │ │ │ │ │ │ │ ├── navigation.component.html
│ │ │ │ │ │ │ │ │ ├── navigation.component.scss
│ │ │ │ │ │ │ │ │ ├── navigation.component.spec.ts
│ │ │ │ │ │ │ │ │ ├── navigation.component.stories.ts
│ │ │ │ │ │ │ │ │ └── navigation.component.ts
│ │ │ │ │ │ │ │ ├── shared-components.module.spec.ts
│ │ │ │ │ │ │ │ └── shared-components.module.ts
│ │ │ │ │ │ │ └── test-setup.ts
│ │ │ │ │ │ ├── tsconfig.json
│ │ │ │ │ │ ├── tsconfig.lib.json
│ │ │ │ │ │ ├── tsconfig.spec.json
│ │ │ │ │ │ └── tslint.json
│ │ │ │ │ ├── nx.json
│ │ │ │ │ ├── package.json
│ │ │ │ │ ├── scully.config.js
│ │ │ │ │ ├── tools/
│ │ │ │ │ │ ├── schematics/
│ │ │ │ │ │ │ └── .gitkeep
│ │ │ │ │ │ └── tsconfig.tools.json
│ │ │ │ │ ├── tsconfig.json
│ │ │ │ │ └── tslint.json
│ │ │ │ ├── preact-app/
│ │ │ │ │ ├── .gitignore
│ │ │ │ │ ├── README.md
│ │ │ │ │ ├── package.json
│ │ │ │ │ └── src/
│ │ │ │ │ ├── .babelrc
│ │ │ │ │ ├── components/
│ │ │ │ │ │ ├── app.js
│ │ │ │ │ │ ├── header/
│ │ │ │ │ │ │ ├── index.js
│ │ │ │ │ │ │ └── style.css
│ │ │ │ │ │ └── info.js
│ │ │ │ │ ├── index.js
│ │ │ │ │ ├── manifest.json
│ │ │ │ │ ├── routes/
│ │ │ │ │ │ ├── about/
│ │ │ │ │ │ │ └── index.js
│ │ │ │ │ │ ├── home/
│ │ │ │ │ │ │ ├── index.js
│ │ │ │ │ │ │ └── style.css
│ │ │ │ │ │ └── profile/
│ │ │ │ │ │ ├── index.js
│ │ │ │ │ │ └── style.css
│ │ │ │ │ ├── style/
│ │ │ │ │ │ └── index.css
│ │ │ │ │ └── tests/
│ │ │ │ │ ├── __mocks__/
│ │ │ │ │ │ └── browserMocks.js
│ │ │ │ │ └── header.test.js
│ │ │ │ ├── react-app/
│ │ │ │ │ ├── .gitignore
│ │ │ │ │ ├── README.md
│ │ │ │ │ ├── package.json
│ │ │ │ │ ├── public/
│ │ │ │ │ │ ├── index.html
│ │ │ │ │ │ └── manifest.json
│ │ │ │ │ └── src/
│ │ │ │ │ ├── App.css
│ │ │ │ │ ├── App.jsx
│ │ │ │ │ ├── LazyRoute.jsx
│ │ │ │ │ ├── index.css
│ │ │ │ │ ├── index.js
│ │ │ │ │ ├── intro/
│ │ │ │ │ │ └── Intro.jsx
│ │ │ │ │ └── main/
│ │ │ │ │ ├── Main.jsx
│ │ │ │ │ ├── kid/
│ │ │ │ │ │ └── Kid.jsx
│ │ │ │ │ └── parent/
│ │ │ │ │ └── Parent.jsx
│ │ │ │ ├── react-app-ts/
│ │ │ │ │ ├── .gitignore
│ │ │ │ │ ├── README.md
│ │ │ │ │ ├── package.json
│ │ │ │ │ ├── public/
│ │ │ │ │ │ └── index.html
│ │ │ │ │ ├── src/
│ │ │ │ │ │ ├── App.css
│ │ │ │ │ │ ├── App.tsx
│ │ │ │ │ │ ├── LazyRoute.tsx
│ │ │ │ │ │ ├── index.css
│ │ │ │ │ │ ├── index.tsx
│ │ │ │ │ │ ├── intro/
│ │ │ │ │ │ │ └── Intro.tsx
│ │ │ │ │ │ ├── main/
│ │ │ │ │ │ │ ├── Main.tsx
│ │ │ │ │ │ │ ├── kid/
│ │ │ │ │ │ │ │ └── Kid.tsx
│ │ │ │ │ │ │ └── parent/
│ │ │ │ │ │ │ └── Parent.tsx
│ │ │ │ │ │ └── registerServiceWorker.ts
│ │ │ │ │ ├── tsconfig.json
│ │ │ │ │ ├── tsconfig.test.json
│ │ │ │ │ └── tslint.json
│ │ │ │ └── unknown/
│ │ │ │ └── package.json
│ │ │ ├── parser.spec.ts
│ │ │ ├── preact-jsx.spec.ts
│ │ │ ├── react-jsx.spec.ts
│ │ │ └── react-tsx.spec.ts
│ │ ├── tsconfig.json
│ │ └── webpack.config.js
│ └── guess-webpack/
│ ├── .gitignore
│ ├── .npmignore
│ ├── README.md
│ ├── api/
│ │ └── index.ts
│ ├── index.ts
│ ├── package.json
│ ├── src/
│ │ ├── aot/
│ │ │ ├── aot.tpl
│ │ │ └── guess-aot.ts
│ │ ├── asset-observer.ts
│ │ ├── compress.ts
│ │ ├── declarations.ts
│ │ ├── ga-provider.ts
│ │ ├── guess-webpack.ts
│ │ ├── prefetch-aot-plugin.ts
│ │ ├── prefetch-plugin.ts
│ │ ├── runtime/
│ │ │ ├── guess.ts
│ │ │ ├── runtime.tpl
│ │ │ └── runtime.ts
│ │ └── utils.ts
│ ├── test/
│ │ ├── e2e/
│ │ │ ├── delegate.spec.ts
│ │ │ ├── next.spec.ts
│ │ │ └── prefetch.spec.ts
│ │ ├── fixtures/
│ │ │ ├── angular/
│ │ │ │ ├── .editorconfig
│ │ │ │ ├── .gitignore
│ │ │ │ ├── README.md
│ │ │ │ ├── angular.json
│ │ │ │ ├── e2e/
│ │ │ │ │ ├── protractor.conf.js
│ │ │ │ │ ├── src/
│ │ │ │ │ │ ├── app.e2e-spec.ts
│ │ │ │ │ │ └── app.po.ts
│ │ │ │ │ └── tsconfig.e2e.json
│ │ │ │ ├── package.json
│ │ │ │ ├── routes.json
│ │ │ │ ├── src/
│ │ │ │ │ ├── app/
│ │ │ │ │ │ ├── app-routing.module.ts
│ │ │ │ │ │ ├── app.component.css
│ │ │ │ │ │ ├── app.component.html
│ │ │ │ │ │ ├── app.component.spec.ts
│ │ │ │ │ │ ├── app.component.ts
│ │ │ │ │ │ ├── app.module.ts
│ │ │ │ │ │ ├── bar/
│ │ │ │ │ │ │ ├── bar.component.ts
│ │ │ │ │ │ │ └── bar.module.ts
│ │ │ │ │ │ ├── foo/
│ │ │ │ │ │ │ ├── baz/
│ │ │ │ │ │ │ │ ├── baz-routing.module.ts
│ │ │ │ │ │ │ │ ├── baz.component.ts
│ │ │ │ │ │ │ │ └── baz.module.ts
│ │ │ │ │ │ │ ├── foo-routing.module.ts
│ │ │ │ │ │ │ ├── foo.component.ts
│ │ │ │ │ │ │ └── foo.module.ts
│ │ │ │ │ │ └── qux/
│ │ │ │ │ │ ├── qux-routing.module.ts
│ │ │ │ │ │ ├── qux.component.ts
│ │ │ │ │ │ └── qux.module.ts
│ │ │ │ │ ├── assets/
│ │ │ │ │ │ └── .gitkeep
│ │ │ │ │ ├── browserslist
│ │ │ │ │ ├── environments/
│ │ │ │ │ │ ├── environment.prod.ts
│ │ │ │ │ │ └── environment.ts
│ │ │ │ │ ├── index.html
│ │ │ │ │ ├── karma.conf.js
│ │ │ │ │ ├── main.ts
│ │ │ │ │ ├── polyfills.ts
│ │ │ │ │ ├── styles.css
│ │ │ │ │ ├── test.ts
│ │ │ │ │ ├── tsconfig.app.json
│ │ │ │ │ ├── tsconfig.spec.json
│ │ │ │ │ └── tslint.json
│ │ │ │ ├── tsconfig.json
│ │ │ │ ├── tslint.json
│ │ │ │ └── webpack.extra.js
│ │ │ ├── delegate/
│ │ │ │ ├── index.html
│ │ │ │ ├── index.js
│ │ │ │ ├── package.json
│ │ │ │ └── webpack.config.js
│ │ │ ├── next/
│ │ │ │ ├── components/
│ │ │ │ │ └── layout.js
│ │ │ │ ├── next.config.js
│ │ │ │ ├── package.json
│ │ │ │ └── pages/
│ │ │ │ ├── about.js
│ │ │ │ ├── contact.js
│ │ │ │ └── index.js
│ │ │ └── prefetch/
│ │ │ ├── about.js
│ │ │ ├── contact.js
│ │ │ ├── home.js
│ │ │ ├── index.html
│ │ │ ├── index.js
│ │ │ ├── package.json
│ │ │ └── webpack.config.js
│ │ └── unit/
│ │ ├── compress.spec.ts
│ │ ├── runtime.spec.ts
│ │ └── utils.spec.ts
│ ├── tsconfig-api.json
│ ├── tsconfig.json
│ └── webpack.config.js
├── renovate.json
├── tsconfig.json
└── tslint.json
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
.DS_Store
.idea
.vscode
build
node_modules
npm-debug.log
temp
tmp-*
.esm-cache/
dist
lerna-debug.log
*.d.ts
================================================
FILE: .travis.yml
================================================
language: node_js
node_js: stable
os: linux
sudo: required
install:
- npm run bootstrap
script:
- npm run build
- npm run test:ci
- npm run e2e
git:
depth: 5
cache:
directories:
- node_modules
================================================
FILE: CONTRIBUTING.md
================================================
# Contributing to Guess.js
Please contribute to this repository if any of the following is true:
- You are interested in improving the Guess.js source, docs or one of our related projects
- You have expertise in community development, communication, or education
- You want open source communities to be more collaborative and inclusive
- You want to help lower the burden to first time contributors
# How to contribute to our source code
Prerequisites:
- familiarity with [GitHub PRs](https://help.github.com/articles/using-pull-requests) (pull requests) and issues
- knowledge of JavaScript and some [TypeScript](https://www.typescriptlang.org/)
## Submitting a Pull Request
All submissions, including submissions by project members, require review. We use Github pull requests for this purpose. See our [developer guide](DEVELOPING.md) for instructions on building the project.
Pull requests (fixes, new features, tests) are a great way to contribute to
the project and help us make it better. Ideally, try to keep your PRs as
focused as possible and keep your commits atomic and readable.
To avoid disappointment when working on a PR, please ask us first in case
someone else is already working on a PR for a change you wished to make. It's always a good idea to file an issue before starting work on a PR unless
it's for something minor (such as a typo fix).
We greatly appreciate any attention to tests. These help us validate that
new work continues to function as expected over time.
# How to contribute to our README or docs
Prerequisites:
- familiarity with [GitHub PRs](https://help.github.com/articles/using-pull-requests) (pull requests) and issues
- knowledge of Markdown for editing `.md` documents
In particular, this community seeks the following types of contributions:
- ideas: participate in an Issues thread or start your own to have your voice
heard
- resources: submit a PR to add to our README with links to related content
- outline sections: help us ensure that this repository is comprehensive. If
there is a topic that is overlooked, please add it, even if it is just a stub
in the form of a header and single sentence. Initially, most things fall into
this category
- write: contribute your expertise in an area by helping us expand the included
content
- copy editing: fix typos, clarify language, and generally improve the quality
of the content
- formatting: help keep content easy to read with consistent formatting
- code: Fix issues or contribute new features to this or any related projects
This contribution guide was inspired by others including contributing to
Google open-source, React, Gulp and Babel.
================================================
FILE: DEVELOPING.md
================================================
# Developer's Guide
This document explains how to build, test, and publish the packages from the monorepo.
## Installation
In order to install all the dependencies run:
```bash
npm run bootstrap
```
This will download all development dependencies for the monorepo and download the dependencies for each individual package. It will also call `lerna bootstrap` which will create symlinks for the cross-package dependencies.
## Build
In order to build the packages run:
```bash
npm run build
```
The command will build all the packages, topologically sorted.
## Publish
To publish the packages, run:
```bash
npm run publish
```
The `publish` script will delegate the execution to `lerna publish` which will take care of updating the dependencies' versions and publishing them to npm.
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2018 Minko Gechev <mgechev@gmail.com> and the Guess
contributors
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
================================================
FILE: README.md
================================================
[](https://travis-ci.com/guess-js/guess)
# Guess.js (alpha)
Libraries and tools for enabling data-driven user-experiences on the web.
## Quickstart
***For Webpack users:***
### :black_circle: Data-driven bundling
Install and configure [GuessPlugin](https://github.com/guess-js/guess/tree/master/packages/guess-webpack) - the Guess.js webpack plugin which automates as much of the setup process for you as possible.
Should you wish to try out the modules we offer individually, the `packages` directory contains three packages:
* [`ga`](https://github.com/guess-js/guess/tree/master/packages/guess-ga) - a module for fetching structured data from the Google Analytics API to learn about user navigation patterns.
* [`parser`](https://github.com/guess-js/guess/tree/master/packages/guess-parser) - a module providing JavaScript framework parsing. This powers the route-parsing capabilities implemented in the Guess webpack plugin.
* [`webpack`](https://github.com/guess-js/guess/tree/master/packages/guess-webpack) - a webpack plugin for setting up predictive fetching in your application. It consumes the `ga` and `parser` modules and offers a large number of options for configuring how predictive fetching should work in your application.
***For non-Webpack users:***
### :black_circle: Data-driven loading
Our [predictive-fetching for sites](https://github.com/guess-js/guess/tree/master/experiments/guess-static-sites) workflow provides a set of steps you can follow to integrate predictive fetching using the Google Analytics API to your site.
This repo uses [Google Analytics](http://analytics.google.com) data to determine which page a user is mostly likely to visit next from a given page. A client-side script (which you'll add to your application) sends a request to the server to get the URL of the page it should fetch, then prefetches this resource.
## Learn More
### What is Guess.js?
Guess.js provides libraries & tools to simplify predictive data-analytics driven approaches to improving user-experiences on the web. This data can be driven from any number of sources, including analytics or [machine learning](https://en.wikipedia.org/wiki/Machine_learning?sa=D&ust=1522637949792000) models. Guess.js aims to lower the friction of consuming and applying this thinking to all modern sites and apps, including building libraries & tools for popular workflows.
Applying predictive data-analytics thinking to sites could be applied in a number of contexts:
* **Predict the next page (or pages) a user is likely to visit and prefetch these pages, improving perceived page load performance and user happiness.**
* Page-level: Prerender/Prefetch the page which is most likely to be visited next
* Bundle-level: Prefetch the bundles associated with the top _N_ pages. On each page navigation, at all the neighbors of the current page, sorted in descending order by the probability to be visited. Fetch assets (JavaScript chunks) for the top N pages, depending on the current connection effective type.
* **Predict the next piece of content (article, product, video) a user is likely to want to view and adjust or filter the user experience to account for this.**
* **Predict the types of widgets an individual user is likely to interact with more (e.g games) and use this data to tailor a more custom experience.**
By collaborating across different touch-points in the ecosystem where data-driven approaches could be easily applied, we hope to generalize common pieces of infrastructure to maximize their applicability in different tech stacks.
### Problems we're looking to solve
* Developers using [`<link rel=prefetch>`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Link_prefetching_FAQ?sa=D&ust=1522637949794000) for future navigations heavily rely on manually reading descriptive analytics to inform their decisions for what to prefetch.
* These decisions are often made at a point in time and..
* (1) are often not revisited as data trends change
* (2) are very limited in how they are used. Implementations will often only prefetch content from a homepage or very small set of hero pages, but otherwise not do this for all of the possible entry points on a site. This can leave performance opportunities on the table.
* (3) Require some amount of confidence about the data being used to drive decisions around using prefetching means that developers may not be adopting it out of worry they will waste bandwidth. `<link rel=prefetch>` is [currently used on 5%](https://www.chromestatus.com/metrics/feature/timeline/popularity/917) of total Chrome pageloads, but this could be higher.
* Implementing predictive analytics is too complex for the average web developer.
* Most developers are unfamiliar with how to leverage the [Google Analytics API](https://developers.google.com/analytics/devguides/reporting/core/v4/?sa=D&ust=1522637949796000) to determine the probability a page will be visited next. We lack:
* (1) Page-level solution: a drop-in client-side solution for prefetching pages a user will likely visit
* (2) Bundling-level solution: a set of plugins/tools that work with today’s JavaScript bundlers (e.g webpack) to cluster and generate the bundles/chunks a particular set of navigation paths could load quicker were they to be prefetched ahead of time.
* Most developers are not yet familiar with how [Machine Learning](https://en.wikipedia.org/wiki/Machine_learning?sa=D&ust=1522637949797000) works. They are generally:
* (1) Unsure how (and why) ML could be integrated into their existing (web) tech stacks
* (2) What the value proposition of [TensorFlow](https://www.tensorflow.org/?sa=D&ust=1522637949797000) is or where solutions like the [CloudML](https://cloud.google.com/ml-engine/?sa=D&ust=1522637949798000) engine fit in. We have an opportunity to simplify the overhead associated with leveraging some of these solutions.
* Best-in-class / low-friction approaches in this space are still slowly emerging and are not yet as accessible to web developers without ML or data-science backgrounds.
* [Machine Learning meets Cloud: Intelligent Prefetching](https://iihnordic.com/blog/machine-learning-meets-the-cloud-intelligent-prefetching/?sa=D&ust=1522637949798000) by IIH Nordic
* Tag Managers like [Google Tag Manager](https://www.google.com/analytics/tag-manager/?sa=D&ust=1522637949799000) can be used to decouple page content from the code tracking how the content is used. This allows web analysts to upgrade the tracking code in real-time with no site downtime. Tag managers allow a general solution for code injection and can be used to deploy intelligent prefetching. The advantages: analytics used to build the model comes from the tag manager. We can also send data live to the predictor without additional tracker overhead. After adding a few (of IIH Nordic’s) tags to a GTM install, a site can start to prefetch resources of the next pages and track load time saving opportunities.
* IIH Nordic moved the predictive prefetching model to a web service the browser queries when a user visits a new page. The service responds to each request and takes advantage of Google Cloud, App Engine and [Cloud ML](https://cloud.google.com/ml-engine/?sa=D&ust=1522637949799000). Their solution chooses the most accurate model, choices include a [Markov model](https://en.wikipedia.org/wiki/Markov_model?sa=D&ust=1522637949800000) or most often a deep neural net in [TensorFlow](https://www.tensorflow.org/?sa=D&ust=1522637949800000).
* With user behavior changing over time, predictive models require updating (training) from time to time. Training a model involves collecting and transforming data and fitting the parameters of the model accordingly. IIH Nordic use Google Cloud to pull data from a customer’s analytics service into a private data bucket in [BigQuery](https://cloud.google.com/bigquery/?sa=D&ust=1522637949800000). They process this data, train and test predictive models, updating the prediction service seamlessly.
* IIH Nordic suggest small/slow sites update their models monthly. Larger sites may need to retrain daily or even hourly for news websites.
* The benefit of training ML models in the cloud is ease of scale as additional machines, GPUs and processors can be added as needed.
* [Machine Learning-Driven Bundling. The Future of JavaScript Tooling](http://blog.mgechev.com/2018/03/18/machine-learning-data-driven-bundling-webpack-javascript-markov-chain-angular-react/?sa=D&ust=1522637949801000) by Minko
#### Initial priority: Improved Performance through Data-driven Prefetching
The first large priority for Guess.js will be improving web performance through predictive prefetching of content.
By building a model of pages a user is likely to visit, given an arbitrary entry-page, a solution could calculate the likelihood a user will visit a given next page or set of pages and prefetch resources for them while the user is still viewing their current page. This has the possibility of improving page-load performance for subsequent page visits as there's a strong chance a page will already be in the user's cache.
### Possible approaches to predictive fetching
In order to predict the next page a user is likely to visit, solutions could use the [Google Analytics API](https://developers.google.com/analytics/devguides/reporting/core/v4/?sa=D&ust=1522637949828000). Google Analytics session data can be used to create a model to predict the most likely page a user is going to visit next on a site. The benefit of this session data is that it can evolve over time, so that if particular navigation paths change, the predictions can stay up to date too.
With the availability of this data, an engine could insert `<link rel="[prerender/prefetch/preload]">` tags to speed up the load time for the next page request. In some tests, such as Mark Edmondson's [Supercharging Page-Loads with R](http://code.markedmondson.me/predictClickOpenCPU/supercharge.html?sa=D&ust=1522637949828000), this led to a 30% improvement in page load times. The approach Mark used in his research involved using GTM tags and machine-learning to train a model for page predictions. This is an idea Mark continued in [Machine Learning meets the Cloud - Intelligent Prefetching](https://iihnordic.com/blog/machine-learning-meets-the-cloud-intelligent-prefetching/?sa=D&ust=1522637949828000).
While this approach is sound, the methodology used could be deemed a little complex. Another approach that could be taken (which is simpler) is attempting to get accurate prediction data from the Google Analytics API. If you ran a report for the [Page](https://developers.google.com/analytics/devguides/reporting/core/dimsmets%23view%3Ddetail%26group%3Dpage_tracking%26jump%3Dga_pagepath?sa=D&ust=1522637949829000) and [Previous Page Path](https://developers.google.com/analytics/devguides/reporting/core/dimsmets%23view%3Ddetail%26group%3Dpage_tracking%26jump%3Dga_previouspagepath?sa=D&ust=1522637949829000) dimension combined with the [Pageviews](https://developers.google.com/analytics/devguides/reporting/core/dimsmets%23view%3Ddetail%26group%3Dpage_tracking%26jump%3Dga_pageviews?sa=D&ust=1522637949830000) and [Exits](https://developers.google.com/analytics/devguides/reporting/core/dimsmets%23view%3Ddetail%26group%3Dpage_tracking%26jump%3Dga_exits?sa=D&ust=1522637949830000) metrics this should provide enough data to wire up prefetches for most popular pages.
#### Machine Learning for predictive fetching
ML could help improve the overall accuracy of a solution's predictions, but is not a necessity for an initial implementation. Predictive fetching could be accomplished by training a model on the pages users are likely to visit and improving on this model over time.
Deep neural networks are particularly good at teasing out the complexities that may lead to a user choosing one page over another, in particular, if we wanted to attempt a version of the solution that was catered to the pages an individual user might visit vs. the pages a "general/median" user might visit next. Fixed page sequences (prev, current, next) might be the easiest to begin dealing with initially. This means building a model that is unique to your set of documents.
Model updates tend to be done periodically, so one might setup a nightly/weekly job to refresh based on new user behaviour. This could be done in real-time, but is likely complex, so doing it periodically might be sufficient. One could imagine a generic model representing behavioural patterns for users on a site that can either be driven by a trained status set, Google Analytics, or a custom description you plugin using a new layer into a router giving the site the ability to predictively fetch future pages, improving page load performance.
### Possible approaches to speculative prefetch
#### Speculative prefetch on page load
Speculative prefetch can prefetch pages likely be navigated to on page load. This assumes the existence of knowledge about the probability a page will need a certain next page or set of pages, or a training model that can provide a data-driven approach to determining such probabilities.
Prefetching on page load can be accomplished in a number of ways, from deferring to the UA to decide when to prefetch resources (e.g at low priority with `<link rel=prefetch>`), during page idle time (via [requestIdleCallback()](https://developer.mozilla.org/en-US/docs/Web/API/Window/requestIdleCallback?sa=D&ust=1522637949834000)()) or at some other interval. No further interaction is required by the user.
#### Speculative prefetch when links come into the viewport
A page could speculatively begin prefetching content when links in the page are visible in the viewport, signifying that the user may have a higher chance of wanting to click on them.
This is an approach used by [Gatsby](https://www.gatsbyjs.org/?sa=D&ust=1522637949834000) (which uses [React](https://reactjs.org/?sa=D&ust=1522637949835000) and [React Router](https://github.com/ReactTraining/react-router?sa=D&ust=1522637949835000)). Their specific implementation is as follows:
* In browsers that support [IntersectionObserver](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API?sa=D&ust=1522637949836000), whenever a `<Link>` component becomes invisible, the link "votes" for the page linked to to be prefetched votes are worth slightly less points each time so links at the top of the page are prioritized over ones lower down
* e.g. the top nav if a page is linked to multiple times, its vote count goes higher the prefetcher takes the top page and starts prefetching resources.
* It's restricted to prefetching one page at a time so as to reduce contention over bandwidth with on page stuff (not a problem on fast networks. If a user visits a page and its resources haven't been fully downloaded, prefetching stops until the page is loaded to ensure the user waits as little time as possible.
#### Speculative prefetch on user interaction
A page could begin speculatively prefetching resources when a user indicates they are interested in some content. This can take many forms, including when a user chooses to hover over a link or some portion of UI that would navigate them to a separate page. The browser could begin fetching content for the link as soon as there was a clear indication of interest. This is an approach taken by JavaScript libraries such as [InstantClick](http://instantclick.io/?sa=D&ust=1522637949837000).
### Risks
#### Data consumption
As with any mechanism for prefetching content ahead of time, this needs to be approached very carefully. A user on a restricted data-plan may not appreciate or benefit as much from pages being fetched ahead of time, in particular if they start to eat up their data. There are mechanisms a site/solution could take to be mindful of this concern, such as respecting the [Save-Data](https://developers.google.com/web/updates/2016/02/save-data?sa=D&ust=1522637949832000) header.
#### Prefetching undesirable pages
Prefetching links to "logout" pages is likely undesirable. The same could be said of any pages that trigger an action on page-load (e.g one-click purchase). Solutions may wish to include a blacklist of URLs which are never prefetched to increase the likelihood of a prefetched page being useful.
#### Web Standards
##### Future of rel=prerender
Some of the attempts to accomplish similar proposals in the past have relied on `<link rel=prerender>`. The Chrome team is currently exploring [deprecating rel=prerender](https://groups.google.com/a/chromium.org/forum/%23!topic/blink-dev/0nSxuuv9bBw?sa=D&ust=1522637949833000) in favor of [NoStatePrefetch](https://docs.google.com/document/d/16VCYGGWau483IMSxODpg5faZny1FJ6vNK2v-BuM5EhU/edit%23?sa=D&ust=1522637949833000) - a lighter version of this mechanism that only prefetches to the HTTP cache but uses no other state of the web platform. A solution should factor in whether it will be relying on the replacement to rel=prerender or using prefetch/preload/other approaches.
There are two key differences between NoStatePrefetch and Prefetch:
1. nostate-prefetch is a mechanism, and `<link rel=prefetch>` is an API. The nostate-prefetch can be requested by other entry points: omnibox prediction, custom tabs, `<link rel=prerender>`.
2. The implementation is different: `<link rel=prefetch>` prefetches one resource, but nostate-prefetch on top of that runs the preload scanner on the resource (in a fresh new renderer), discovers subresources and prefetches them as well (without recursing into preload scanner).
### Relevant Data Analytics
There are [three primary types](https://halobi.com/blog/descriptive-predictive-and-prescriptive-analytics-explained/?sa=D&ust=1522637949802000) of data analytics worth being aware of in this problem space: descriptive, predictive and prescriptive. Each type is related and help teams leverage different kinds of insight.
#### Descriptive - what has happened?
Descriptive analytics summarizes raw data and turns it into something interpretable by humans. It can look at past events, regardless of when the events have occurred. Descriptive analytics allow teams to learn from past behaviors and this can help them influence future outcomes. Descriptive analytics could determine what pages on a site users have previously viewed and what navigation paths they have taken given any given entry page.
#### Predictive - what will happen?
Predictive analytics “predicts” what can happen next. Predictive analytics helps us understand the future and gives teams actionable insights using data. It provides estimates of the likelihood of a future outcome being useful. It’s important to keep in mind, few algorithms can predict future events with complete accuracy, but we can use as many signals that are available to us as possible to help improve baseline accuracy. The foundation of predictive analytics is based on probabilities we determine from data. Predictive analytics could predict the next page or set of pages a user is likely to visit given an arbitrary entry page.
#### Prescriptive - what should we do?
Prescriptive analytics enables prescribing different possible actions to guide towards a solution. Prescriptive analytics provides advice, attempting to quantify the impact future decisions may have to advise on possible outcomes before these decisions are made. Prescriptive analytics aims to not just predict what is going to happen but goes further; informing why it will happen and providing recommendations about actions that can take advantage of such predictions. Prescriptive analytics could predict the next page a user will visit, but also suggest actions such as informing you of ways you can customize their experience to take advantage of this knowledge.
### Relevant Prediction Models
#### Markov Models
The key objective of a prediction model in the prefetching problem space is to identify what the subsequent requests a user may need, given a specific page request. This allows a server or client to pre-fetch the next set of pages and attempt to ensure they are in a user’s cache before they directly navigate to the page. The idea is to reduce overall loading time. When this is implemented with care, this technique can reduce page access times and latency, improving the overall user experience.
[Markov models](https://en.wikipedia.org/wiki/Markov_model?sa=D&ust=1522637949805000) have been widely used for researching and understanding stochastic (random probability distribution) process [[Ref](http://citeseerx.ist.psu.edu/viewdoc/download?doi%3D10.1.1.436.2396%26rep%3Drep1%26type%3Dpdf?sa=D&ust=1522637949806000), [Ref](https://www.researchgate.net/publication/266568034_Effective_Web_Cache_Pre-fetching_technique_using_Markov_Chain?sa=D&ust=1522637949806000)] . They have been demonstrated to be well-suited for modeling and predicting a user’s browsing behavior. The input for these problems tends to be the sequence of web pages accessed by a user or set of users (site-wide) with the goal of building Markov models we can use to model and predict the pages a user will most likely access next. A Markov process has states representing accessed pages and edges representing transition probabilities between states which are computed from a given sequence in an analytics log. A trained Markov model can be used to predict the next state given a set of k previous states.
In some applications, first-order Markov models aren’t as accurate in predicting user browsing behaviors as these do not always look into the past to make a distinction between different patterns that have been observed. This is one reason higher-order models are often used. These higher-order models have limitations with state-space complexity, less broad coverage and sometimes reduced prediction accuracy.
#### All-Kth-Order Markov Model
One way [[Ref](http://www.siam.org/meetings/sdm01/pdf/sdm01_04.pdf?sa=D&ust=1522637949807000)] to overcome this problem is to train varying order Markov models, which we then use during the prediction phase. This was attempted in the [All-Kth-Order Markov model](http://www.siam.org/meetings/sdm01/pdf/sdm01_04.pdf?sa=D&ust=1522637949808000) proposed in this [Ref](https://dl.acm.org/citation.cfm?id%3D1251493?sa=D&ust=1522637949808000). This can make state-space complexity worse, however. Another approach is to identify frequent access patterns (longest repeating subsequences) and use this set of sequences for predictions. Although this approach can have an order of magnitude reduction on state-space complexity, it can reduce prediction accuracy.
#### Selective Markov Models
[Selective Markov models](http://www.siam.org/meetings/sdm01/pdf/sdm01_04.pdf?sa=D&ust=1522637949808000) (SMM) which only store some states within the model have also been proposed as a solution to state-space complexity tradeoffs. They begin with a All-Kth-Order Markov Model - a post-pruning approach is then used to prune states that are not expected to be accurate predictors. The result of this is a model which has the same prediction power of All-Kth-Order models with less space complexity and higher prediction accuracy. In [Deshpane and Karpis](http://www.siam.org/meetings/sdm01/pdf/sdm01_04.pdf?sa=D&ust=1522637949809000), different criteria to prune states in the model before prediction (frequency, confidence, error) are looked at.
#### Semantic-pruned Selective Markov Models
In [Mabroukeh and Ezeife](http://ieeexplore.ieee.org/document/5360449/?reload%3Dtrue?sa=D&ust=1522637949809000), the performance of semantic-rich 1st and 2nd order Markov models was studied and compared with that of higher-order SMM and semantic-pruned SMM. They discovered that semantic-pruned SMM have a 16% smaller size than frequency-pruned SMM and provide nearly an equal accuracy.
#### Clustering
Observing navigation patterns can allow us to analyze user behavior. This approach requires access to user-session identification, clustering sessions into similar clusters and developing a model for prediction using current and earlier access patterns. Much of the previous work in this field has relied on clustering schemes like the [K-means clustering](https://en.wikipedia.org/wiki/K-means_clustering?sa=D&ust=1522637949810000) technique with [Euclidean distance](https://en.wikipedia.org/wiki/Euclidean_distance?sa=D&ust=1522637949810000) for improving confidence of predictions. One of the drawbacks to using K-means is difficulty deciding on the number of clusters, selecting the initial random center and the order of page visits is not always considered. [Kumar et al](http://ieeexplore.ieee.org/document/7519368/?sa=D&ust=1522637949811000) investigated this, proposing a hierarchical clustering technique with a modified [Levenshtein distance](https://en.wikipedia.org/wiki/Levenshtein_distance?sa=D&ust=1522637949811000), pagerank using access time length, frequency and higher order Markov models for prediction.
### Research review
Many of the papers referenced in the following section are centered around the Markov model, association rules and clustering. Papers highlighting relevant work related to pattern discovery for evolving page prediction accuracy are our focus.
#### Sarukkai [2000] “[Link prediction and path analysis using Markov chains](https://www.sciencedirect.com/science/article/pii/S138912860000044X?sa=D&ust=1522637949813000)”.
Uses first-order Markov models to model the sequence of web-pages requested by a user for predicting the next page they are likely to access. Markov chains allow the system to dynamically model URL access patterns observed in navigation logs based on previous state. A “personalized” Markov model is trained for each user and used to predict a user’s future sessions. In practice, it’s overly expensive to construct a unique model for each user and the cost of scaling this becomes more challenging when a site has a large user-base.
#### Chun-Jung Lin [2005] ”[Using Hidden Markov Model to Predict the Surfing User’s Intention of Cyber Purchase on the Web](https://www.researchgate.net/publication/260319657_Using_Hidden_Markov_Model_to_Predict_the_Surfing_User's_Intention_of_Cyber_Purchase_on_the_Web?sa=D&ust=1522637949814000)”
First paper to investigate Hidden Markov Models (HMM). Author collected web server logs, pruned the data and patched the paths users passed by. Based on HMM, author constructed a specific model for web browsing that predicts whether the users have the intention to purchase in real-time. Related measures, like speeding up the operation and their impact when in a purchasing mode are investigated.
#### Elli Voudigari [2010-2011] ” [A Framework for Web Page Rank Prediction](https://link.springer.com/chapter/10.1007/978-3-642-23960-1_29?sa=D&ust=1522637949814000)”.
Proposes a framework to predict ranking positions of a page based on their previous rankings. Assuming a set of successive Top-K rankings, the author identifies predictors based on different methodologies. Prediction quality is quantified as the similarity between predicted and actual rankings. Exhaustive experiments were performed on a real-world large scale dataset for both global and query-based top-K rankings. A variety of existing similarity measures for comparing Top-K ranked lists including a novel one captured in the paper.
#### Mogul [1996] “ [Using predictive prefetching to improve World Wide Web latency](https://www.semanticscholar.org/paper/Using-predictive-prefetching-to-improve-World-Wide-Padmanabhan-Mogul/4d7e5e430bec3db4044b13ce8da7411f09c745f3?sa=D&ust=1522637949815000)”.
Proposes using N-hop Markov models to predict the next web page users are likely to access. Pattern matches the user’s current access sequence with the user’s historical web access sequences to improve the prediction accuracy for prefetches.
#### Borges, Levene [2007] “[Evaluating Variable-Length Markov Chain Models for Analysis of User Web Navigation Sessions](http://ieeexplore.ieee.org/document/4118703/?sa=D&ust=1522637949816000)”.
Proposes dynamic clustering-based methods to increase Markov model accuracy in representing a collection of web navigation sessions. Uses a state cloning concept to duplicate states in a way separating in-links whose corresponding second-order probabilities diverge. The method proposed includes a clustering technique determining a way to assign in-links with similar second-order probabilities to the same clone.
#### Banu Deniz Gunel [2010] ” [Investigating the Effect of Duration, Page Size and Frequency on Next Page Recommendation with Page Rank Algorithm](https://www.researchgate.net/publication/268366760_Investigating_the_Effect_of_Duration_Page_Size_and_Frequency_on_Next_Page_Recommendation_with_Page_Rank_Algorithm?sa=D&ust=1522637949817000)”.
Extends the use of a page-rank algorithm with numerous navigational attributes: size of the page, duration time of the page, duration of transition (two page visits sequentially), frequency of page and transition. Defines a Duration Based Rank (DPR) and Popularity Based Page Rank (PPR). Author looked at the popularity of transitions and pages using duration information, using it with page size and visit frequency. Using the popularity value of pages, this paper attempts to improve conventional page rank algorithms and model a next page prediction under a given Top-N value.
## References
* [Supercharging page-loads with R](http://code.markedmondson.me/predictClickOpenCPU/supercharge.html?sa=D&ust=1522637949840000)
* [Using Google Analytics to predict clicks](https://www.noisetosignal.io/2016/11/using-google-analytics-to-predict-clicks-and-speed-up-your-website/?sa=D&ust=1522637949841000)
* [Gatsby's Link](https://github.com/gatsbyjs/gatsby/tree/master/packages/gatsby-link?sa=D&ust=1522637949841000)
* [Eve Dynamic Prerender](https://wordpress.org/plugins/eve-dynamic-prerender/?sa=D&ust=1522637949841000)
* [InstartLogic - Multi-page Predictive Prefetching](https://www.instartlogic.com/blog/predicting-future-multi-page-predictive-prefetching?sa=D&ust=1522637949841000)
* [Sirko Engine](https://github.com/sirko-io/engine?sa=D&ust=1522637949842000) - relies on Service Worker
<h2>Team</h2>
<table>
<tbody>
<tr>
<td align="center" valign="top">
<img width="100" height="100" src="https://github.com/mgechev.png?s=150">
<br>
<a href="https://github.com/mgechev">Minko Gechev</a>
</td>
<td align="center" valign="top">
<img width="100" height="100" src="https://github.com/addyosmani.png?s=150">
<br>
<a href="https://github.com/addyosmani">Addy Osmani</a>
</td>
<td align="center" width="20%" valign="top">
<img width="100" height="100" src="https://github.com/khempenius.png?s=150">
<br>
<a href="https://github.com/khempenius">Katie Hempenius</a>
</td>
<td align="center" valign="top">
<img width="100" height="100" src="https://github.com/kyleamathews.png?s=150">
<br>
<a href="https://github.com/kyleamathews">Kyle Mathews</a>
</td>
</tr>
</tbody>
</table>
================================================
FILE: experiments/guess-static-sites/.gitignore
================================================
*.env
*.pem
*.p12
node_modules/
*.swp
================================================
FILE: experiments/guess-static-sites/README.md
================================================
# guess-static-sites
Guess.js for non-Webpack sites.
*Automatic, dynamic, intelligent prefetching for faster page loads.*
:heavy_check_mark: **Automatic:** Once you've setup predictive fetching you'll automatically be using it on all your pages. (No more forgetting to take advantage of prefetch.)
:heavy_check_mark: **Dynamic:** As your site changes, prefetch links will adjust accordingly. (No more hardcoded prefetch URLs.)
:heavy_check_mark: **Intelligent:** Predictive fetching uses the client's connection type to determine whether a resource should be prefetched.
## How guess-static-sites works
This directory uses Google Analytics data to determine which page a user is mostly likely to visit next from a given page (***generatePredictions.js***).
A client-side script (which you'll add to your application) sends a request to the server you are running to get the URL of the page it should fetch, it then prefetches this resource (***script.js & server.js***).
If a user is on a poor connection, prefetching will only occur if there's a high level of certainty that a user will go to a particular page next. If a client is using the Save-Data header, no prefetching will occur.
## Setup
After downloading the Guess.js repo, cd to this directory and install the dependencies:
```
$ cd experiments/guess-static-sites
$ npm install
```
## A) Server setup
Use this command to run the server:
```
$ node server.js
```
## B) Script setup
1. Add predictiveFetching.js to your pages.
2. In predictiveFetching.js, replace ```'http://YOUR_SERVER_ENDPOINT/'``` with the server endpoint you'll be using.
## C) Database setup
**Prerequisites:**
- A Google Analytics account
- Mongo installed on your computer/server [(Instructions)](https://docs.mongodb.com/manual/installation/)
This is the final, and lengthiest, part of the setup process - but it should only take about 5-10 minutes to complete if you already have Mongo and a Google Analytics account. Afterwards you'll be ready to consume and analyze your Google Analytics data.
### Create Your Credentials
#### i) Create a Service Account
1. Go to the [Credentials](https://console.developers.google.com/apis/credentials) page in the Google APIs console.
2. If you don't have an existing Google Cloud project, click "Create" to create a new project. Otherwise, use the dropdown in the upper left corner to select the existing project that you'd like to use.
3. Select "Service Account key" from the "Create credentials" dropdown.
4. Fill out the form for creating a service account key:
- **Service account dropdown:** Select "New Service Account".
- **Service account name:** Give your service account a name.
- **Role:** Select "Service Account User" ("Service Accounts" > "Service Account User").
- **Service account ID:** This field will automatically be pre-filled, but you can change this if you would like.
- **Key type:** Select P12 key.
5. Click Create.
#### ii) Setup Your Private Key
Your private key should have started downloading when you clicked the "Create" button for creating your service account.
1. Note the private key password. You'll be prompted for this password in Step 3.
2. Move this key into the directory for this project.
3. Generate a *.p12 certificate by running this command from the directory for this project:
```
$ openssl pkcs12 -in *.p12 -out key.pem -nodes -clcerts
```
### Configure Google Analytics
#### i) Add service account to Google Analytics
The service account that you just created needs to be added as a user to your Google analytics account.
1. Login to your [Google Analytics](https://analytics.google.com/analytics/web/) account.
2. Add a new user. (Admin > User Management > + > Add New Users)
- **Email Address** The email address of the service account you created. It should look something like this: example@example-project-123456.iam.gserviceaccount.com.
- **Permissions:** Select "Read & Analyze."
*Note: A service account can only be associated with one Google Analytics account at a time. Thus, if you want to use predictive fetching on multiple sites, you'll need to create a separate service account for each.*
#### ii) Enable the Google Analytics Reporting API
You can enable this [here.](https://console.developers.google.com/flows/enableapi?apiid=analyticsreporting.googleapis.com&credential=client_key)
### Create your .env file
This file will hold your confidential configuration details.
#### i) Create the file
```
$ touch .env
```
#### ii) Add your information
Your file should look like this (replace with your own values):
```bash
VIEW_ID=12345678
SERVICE_ACCOUNT_EMAIL=example@example-project-123456.iam.gserviceaccount.com
```
To find your view ID, go to [Google Analytics](https://analytics.google.com/analytics/web/).
Click the accounts dropdown (it's located in the upper lefthand corner of the screen, right next to the Google Analytics logo). The dialog that opens will have three columns: Analytics Accounts, Properties & Apps, & Views. The far right column ("Views") will contain the View ID for your site.
### Generate predictions
#### i) Start mongod
If mongod is not running, start it:
```
$ mongod
```
#### ii) Run script
```
$ node generatePredictions.js
```
If this is successful, you should see something like this in the console for each page:
```
{ pagePath: 'dogs/poodle.html',
pageviews: 300,
exits: 200,
nextPageviews: 100,
nextExits: 100,
nextPages:
[ { pagePath: '/dogs/puppies/', pageviews: 100 } ],
percentExits: 0.6666666666666666,
topNextPageProbability: 0.3333333333333333 }
```
You can also explore the results in Mongo:
```
$ mongo
$ use guessjs_dev
$ db.predictions.find()
```
#### iii) (Optional) Setup a cron job
It is recommended that you set up a cron job to periodically re-run generatePredictions.js. This will ensure that the prefetch links are as accurate as possible. The ideal frequency of this cron job depends on how frequently your site changes.
You can also experiment with the date range of data that is used to generate predictions. By default, GuessJS uses the last 30 days of traffic to generate predictions, but this value can be changed (this is located in file: *src/queryParams.js*). It's important to have a sufficiently large data set, so the ideal date range will largely depend on a site's traffic volume. A very high-traffic site might find that using the last 1-7 days of traffic works best, while a low-traffic site find that using the last 30 days works best.
================================================
FILE: experiments/guess-static-sites/config.js
================================================
const env = process.env.NODE_ENV
require('dotenv').config()
const dev = {
auth: {
keyFileName: 'key.pem',
viewID: process.env.VIEW_ID,
serviceAccountEmail: process.env.SERVICE_ACCOUNT_EMAIL
},
db: {
mongoURL: 'mongodb://localhost:27017/guessjs_dev'
},
server: {
port: 3000,
url: 'http://localhost:3000'
}
}
const test = {
auth: {
keyFileName: 'key.pem',
viewID: process.env.VIEW_ID,
serviceAccountEmail: process.env.SERVICE_ACCOUNT_EMAIL
},
db: {
mongoURL: 'mongodb://localhost:27017/guessjs_test'
},
server: {
port: 3000,
url: 'http://localhost:3000'
}
}
const prod = {
auth: {
keyFileName: 'key.pem',
viewID: process.env.VIEW_ID,
serviceAccountEmail: process.env.SERVICE_ACCOUNT_EMAIL
},
db: {
mongoURL: 'mongodb://localhost:27017/guessjs_prod'
},
server: {
port: 3000,
url: 'http://localhost:3000'
}
}
const config = {
dev,
test,
prod
}
module.exports = config[env] || config['dev']
================================================
FILE: experiments/guess-static-sites/generatePredictions.js
================================================
/*
This scripts retrieves recent reporting data from Google Analytics
and uses this to determine the "Most Likely Next Page" for each page on your site.
This data is saved in Mongo.
*/
const {google} = require('googleapis')
const fs = require('fs')
const queryParams = require('./src/queryParams')
const parser = require('./src/parser')
const config = require('./config')
const path = require('path')
const authClient = new google.auth.JWT({
email: config.auth.serviceAccountEmail,
key: fs.readFileSync(path.join(__dirname, config.auth.keyFileName), 'utf8'),
scopes: ['https://www.googleapis.com/auth/analytics.readonly']
})
const getData = async (authClient) => {
await authClient.authorize()
const analytics = google.analyticsreporting({
version: 'v4',
auth: authClient
})
const response = await analytics.reports.batchGet(queryParams)
await parser.saveReports(response.data.reports)
process.exit()
}
getData(authClient)
================================================
FILE: experiments/guess-static-sites/package.json
================================================
{
"name": "guess-static-sites",
"version": "1.0.0",
"description": "",
"main": "server.js",
"scripts": {
"generate-predictions-tests": "NODE_ENV=test mocha ./test/gaClientTests.js",
"client-tests": "NODE_ENV=test mocha ./test/clientTests.js",
"server-tests": "NODE_ENV=test mocha ./test/serverTests.js",
"build": "babel src/client.js --out-file dist/client.js --presets minify"
},
"author": "",
"license": "ISC",
"dependencies": {
"body-parser": "^1.18.2",
"dotenv": "^8.0.0",
"express": "^4.16.3",
"fs": "0.0.2",
"googleapis": "^67.0.0",
"mongoose": "^5.0.14"
},
"devDependencies": {
"babel-cli": "6.26.0",
"babel-minify": "0.5.1",
"babel-preset-es2015": "6.24.1",
"babel-preset-minify": "0.5.1",
"chai": "4.3.0",
"cors": "2.8.5",
"http-server": "0.12.1",
"mocha": "8.3.1",
"path": "0.12.7",
"puppeteer": "7.1.0",
"standard": "16.0.3",
"supertest": "6.1.3"
}
}
================================================
FILE: experiments/guess-static-sites/predictiveFetching.js
================================================
const ENDPOINT = 'http://YOUR_SERVER_ENDPOINT/'
const setCookie = function (obj) {
const cookieStr = obj.name + '=' + obj.value + '; max-age=' + obj.maxAge
document.cookie = cookieStr
}
const appendLinkTo = function (url) {
const linkTag = document.createElement('link')
linkTag.rel = 'prefetch'
linkTag.href = url
document.head.appendChild(linkTag)
}
const getConnectionType = function () {
return window.navigator.connection ? window.navigator.connection.effectiveType : ''
}
const getUserFlow = function () {
const result = []
document.cookie.split(';').forEach(c => {
const prefix = c.trim().substr(0, 3)
if (prefix === 'pf_') {
result.push(c.trim())
}
})
return result
}
if (!document.hidden) {
xhr = new XMLHttpRequest()
xhr.open('POST', ENDPOINT)
xhr.setRequestHeader('Content-Type', 'application/json;charset=UTF-8')
xhr.onreadystatechange = function () {
if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) {
const data = JSON.parse(xhr.responseText)
if (data['prefetchPath'] !== '') {
appendLinkTo(window.location.origin + data.prefetchPath)
}
setCookie({
name: 'pf_' + data['pageViewId'],
value: Math.round(new Date() / 1000),
maxAge: 300
})
}
}
const requestBody = {
'pagePath': window.location.pathname,
'userFlow': getUserFlow(),
'clientInfo': {
'connectionType': getConnectionType(),
'platform': window.navigator.platform,
'language': window.navigator.language
}
}
xhr.send(JSON.stringify(requestBody))
}
================================================
FILE: experiments/guess-static-sites/server.js
================================================
const express = require('express')
const bodyParser = require('body-parser')
const cors = require('cors')
const mongoose = require('mongoose')
const Prediction = require('./src/models/prediction')
const PageView = require('./src/models/pageView')
const config = require('./config')
const app = express()
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: true }))
app.use(cors())
app.all('/', function (req, res, next) {
res.header('Access-Control-Allow-Origin', '*')
res.header('Access-Control-Allow-Methods', 'PUT, GET, POST, DELETE, OPTIONS')
res.header('Access-Control-Allow-Headers', 'X-Requested-With, Content-Type')
next()
})
const certaintyThresholdsByConnectionType = {
'slow-2g': 0.95,
'2g': 0.9,
'3g': 0.5,
'4g': 0.2
}
const shouldPrefetch = function (request, prediction) {
if (prediction === null) { return false }
if (request.header('Save-Data')) { return false }
const connectionType = request.body.clientInfo.connectionType
const threshold = certaintyThresholdsByConnectionType[connectionType]
if (threshold === undefined) {
return false
} else {
return parseFloat(prediction.nextPageCertainty) > parseFloat(threshold)
}
}
const getPreviousPageId = function (cookies) {
let latestTimestamp = 0
let latestId
cookies.forEach(c => {
const timestamp = parseInt(c.split('=')[1])
if (timestamp > latestTimestamp) {
latestTimestamp = timestamp
latestId = c.substring(c.indexOf('_') + 1, c.indexOf('='))
}
})
return latestId
}
app.post('/', async (req, res) => {
mongoose.connect(config.db.mongoURL)
const prediction = await Prediction.findOne({'pagePath': req.body['pagePath']})
const prefetchPath = shouldPrefetch(req, prediction) ? prediction['nextPagePath'] : ''
const pageView = await PageView.create({
pagePath: req.body['pagePath'],
clientInfo: req.body['clientInfo'],
userFlow: req.body['userFlow'],
prefetchPath: prefetchPath
})
// Update data about previous page view
if (req.body.userFlow.length > 0) {
const id = getPreviousPageId(req.body.userFlow)
await PageView.findByIdAndUpdate(id, {'actualNextPagePath': req.body.pagePath})
}
res.json({
'pageViewId': pageView._id,
'prefetchPath': prefetchPath
})
})
app.listen(config.server.port)
module.exports = app
================================================
FILE: experiments/guess-static-sites/src/models/pageView.js
================================================
const mongoose = require('mongoose')
const pageViewSchema = mongoose.Schema({
pagePath: String,
clientInfo: Object,
userFlow: Array,
prefetchPath: String,
actualNextPagePath: String
})
const PageView = mongoose.model('PageView', pageViewSchema)
module.exports = PageView
================================================
FILE: experiments/guess-static-sites/src/models/prediction.js
================================================
const mongoose = require('mongoose')
const predictionSchema = mongoose.Schema({
pagePath: String,
nextPagePath: String,
nextPageCertainty: Number
})
const Prediction = mongoose.model('Prediction', predictionSchema)
module.exports = Prediction
================================================
FILE: experiments/guess-static-sites/src/parser.js
================================================
const mongoose = require('mongoose')
const Prediction = require('./models/prediction')
const config = require('../config')
// Generates & saves predictions based off Google Analytics response
const saveReports = async (reports) => {
let [report] = reports
let {rows} = report.data
const data = {}
for (let row of rows) {
let [previousPagePath, pagePath] = row.dimensions
let pageviews = +row.metrics[0].values[0]
let exits = +row.metrics[0].values[1]
if (/\?.*$/.test(pagePath) || /\?.*$/.test(previousPagePath)) {
pagePath = pagePath.replace(/\?.*$/, '')
previousPagePath = previousPagePath.replace(/\?.*$/, '')
}
// Ignore pageviews where the current and previous pages are the same.
if (previousPagePath == pagePath) continue
if (previousPagePath != '(entrance)') {
data[previousPagePath] = data[previousPagePath] || {
pagePath: previousPagePath,
pageviews: 0,
exits: 0,
nextPageviews: 0,
nextExits: 0,
nextPages: {}
}
data[previousPagePath].nextPageviews += pageviews
data[previousPagePath].nextExits += exits
if (data[previousPagePath].nextPages[pagePath]) {
data[previousPagePath].nextPages[pagePath] += pageviews
} else {
data[previousPagePath].nextPages[pagePath] = pageviews
}
}
data[pagePath] = data[pagePath] || {
pagePath: pagePath,
pageviews: 0,
exits: 0,
nextPageviews: 0,
nextExits: 0,
nextPages: {}
}
data[pagePath].pageviews += pageviews
data[pagePath].exits += exits
}
// Converts each pages `nextPages` object into a sorted array.
Object.keys(data).forEach((pagePath) => {
const page = data[pagePath]
page.nextPages = Object.keys(page.nextPages)
.map((pagePath) => ({
pagePath,
pageviews: page.nextPages[pagePath]
}))
.sort((a, b) => b.pageviews - a.pageviews)
})
// Creates a sorted array of pages from the data object.
const pages = Object.keys(data)
.filter((pagePath) => data[pagePath].nextPageviews > 0)
.map((pagePath) => {
const page = data[pagePath]
const {exits, nextPageviews, nextPages} = page
page.percentExits = exits / (exits + nextPageviews)
page.topNextPageProbability =
nextPages[0].pageviews / (exits + nextPageviews)
return page
})
.sort((a, b) => {
// return b.topNextPageProbability - a.topNextPageProbability
return b.pageviews - a.pageviews
})
for (let page of pages) {
// TODO - remove console logs
console.log(page)
console.log('\n')
}
await savePagesToDatabase(pages)
}
// Adds each page (and its associated prediction) to the database.
// If a page already exists, its record is updated based on the most recent Google Analytics data.
const savePagesToDatabase = async (pages) => {
mongoose.connect(config.db.mongoURL)
for (let page of pages) {
const prediction = {
pagePath: page.pagePath,
nextPagePath: page.nextPages[0] ? page.nextPages[0].pagePath : '',
nextPageCertainty: page.nextPages[0] ? page.topNextPageProbability : ''
}
await Prediction.update({'pagePath': prediction.pagePath}, prediction, {'upsert': true})
}
}
module.exports = {saveReports: saveReports}
================================================
FILE: experiments/guess-static-sites/src/queryParams.js
================================================
const config = require('../config')
const queryParams = {
resource: {
reportRequests: [
{
viewId: config.auth.viewID,
dateRanges: [{startDate: '30daysAgo', endDate: 'yesterday'}],
metrics: [
{expression: 'ga:pageviews'},
{expression: 'ga:exits'}
],
dimensions: [
{name: 'ga:previousPagePath'},
{name: 'ga:pagePath'}
],
orderBys: [
{fieldName: 'ga:previousPagePath', sortOrder: 'ASCENDING'},
{fieldName: 'ga:pageviews', sortOrder: 'DESCENDING'}
],
pageSize: 10000
}
]
}
}
module.exports = queryParams
================================================
FILE: experiments/guess-static-sites/test/clientTests.js
================================================
// To run tests: $ npm run script-tests
// Server responses are stubbed, but test server needs to be running
// ($ NODE_ENV=test node ./fixtures/server.js)
// Mongod should be running
// Tests run against dist/client.js; to regenerate this: $ npm run build
const puppeteer = require('puppeteer')
const { expect } = require('chai')
const globalVariables = {'browser': global['browser'], 'expect': global['expect']}
const should = require('chai').should()
// TODO - Use config
const SERVER_URL = 'http://localhost:3000/'
const PAGE_URL = 'http://localhost:8080/test.html'
before(async () => {
global.expect = expect
global.browser = await puppeteer.launch({
headless: false,
slowMo: 100,
timeout: 10000
})
})
after(() => {
browser.close()
global.browser = globalVariables.browser
global.expect = globalVariables.expect
})
describe('Predictve fetching script ', function () {
let page
before(async function () {
page = await browser.newPage()
await page._client.send('Network.clearBrowserCookies')
page.on('console', msg => console.log('PAGE LOG:', msg.text()))
})
after(async function () {
await page.close()
})
describe('makes a request', async function () {
it('should have the correct request body', async function () {
let requests = []
page.on('request', interceptedRequest => {
requests.push(interceptedRequest)
})
const unrelatedCookie = {
'name': 'some_other_cookie',
'value': 'abc',
'domain': 'localhost',
'path': '/test.html',
'expires': (Date.now() / 1000) + 100
}
const cookie1 = {
'name': 'pf_cookie1',
'value': '111',
'domain': 'localhost',
'path': '/test.html',
'expires': (Date.now() / 1000) + 100
}
const cookie2 = {
'name': 'pf_cookie2',
'value': '222',
'domain': 'localhost',
'path': '/test.html',
'expires': (Date.now() / 1000) + 100
}
await page.setCookie(unrelatedCookie)
await page.setCookie(cookie1)
await page.setCookie(cookie2)
await page.goto(PAGE_URL)
request = requests.find(req => {
let URL = SERVER_URL
return req.url() === URL && req.method() === 'POST'
})
page.waitForNavigation({waitUntil: 'networkidle2'})
expect(request).to.exist
const actualResponseBody = JSON.parse(request.postData())
expect(actualResponseBody['pagePath']).to.equal('/test.html')
// not.be.empty (vs. a particular value like "4g" or "MacIntel")
// is used so these tests will work cross-environment
expect(actualResponseBody['clientInfo']['connectionType']).to.not.be.empty
expect(actualResponseBody['clientInfo']['platform']).to.not.be.empty
expect(actualResponseBody['clientInfo']['language']).to.not.be.empty
expect(actualResponseBody['userFlow'].length).to.equal(2)
expect(actualResponseBody['userFlow']).to.contain('pf_cookie1=111')
expect(actualResponseBody['userFlow']).to.contain('pf_cookie2=222')
})
})
describe('request callback', function () {
const setupResponse = async (page, responseBody) => {
await page.setRequestInterception(true)
page.on('request', req => {
if (req.url() === SERVER_URL && req.method() === 'POST') {
req.respond({
status: 200,
contentType: 'application/json',
headers: {'Access-Control-Allow-Origin': '*'},
body: JSON.stringify(responseBody)
})
} else {
req.continue()
}
})
}
before(async function () {
page = await browser.newPage()
setupResponse(page, {'pageViewId': '123', 'prefetchPath': ''})
await page._client.send('Network.clearBrowserCookies')
await page.goto(PAGE_URL)
await page.waitFor(100)
})
describe("when there's no matching prefetch", function () {
it('should not append a link', async function () {
const link = await page.evaluate(() => {
return document.querySelector('link')
})
expect(link).to.be.null
})
it('should set prefetch cookies', async function () {
const cookies = await page.cookies()
expect(cookies.length).to.equal(1)
expect(cookies[0].name).to.equal('pf_123')
const currentTime = new Date() / 1000
// Buffer user to account for slight timestamp difference
// between when cookie was created and when this test runs
const buffer = 5
const cookieValue = parseInt(cookies[0].value)
expect(cookieValue).to.be.closeTo(currentTime, buffer)
const cookieMaxAge = 300
expect(cookies[0].expires).to.be.closeTo(currentTime + cookieMaxAge, buffer)
})
})
describe("when there's a matching prefetch", function () {
before(async function () {
page = await browser.newPage()
setupResponse(page, {'pageViewId': '123', 'prefetchPath': '/nextpage.html'})
await page._client.send('Network.clearBrowserCookies')
await page.goto(PAGE_URL)
await page.waitFor('link')
})
it('should append a link', async function () {
const link = await page.evaluate(() => {
return document.querySelector('link')
})
expect(link).to.exist
})
it('should use prefetch', async function () {
const resourceHint = await page.evaluate(() => {
return document.querySelector('link').rel
})
expect(resourceHint).to.equal('prefetch')
})
it('should load the correct resource', async function () {
const href = await page.evaluate(() => {
return document.querySelector('link').href
})
// Regex matches that "/nextpage.html" apears at the end of the string
expect(href).to.match(/\/nextpage\.html$/)
})
it('should set prefetch cookies', async function () {
const cookies = await page.cookies()
expect(cookies[0].name).to.equal('pf_123')
const currentTime = new Date() / 1000
// Buffer user to account for slight timestamp difference
// between when cookie was created and when this test runs
const buffer = 5
const cookieValue = parseInt(cookies[0].value)
expect(cookieValue).to.be.closeTo(currentTime, buffer)
const cookieMaxAge = 300
expect(cookies[0].expires).to.be.closeTo(currentTime + cookieMaxAge, buffer)
})
})
})
})
================================================
FILE: experiments/guess-static-sites/test/fixtures/gaResponse.json
================================================
{"testData": [{
"columnHeader": {
"dimensions": ["ga:previousPagePath", "ga:pagePath"],
"metricHeader": {
"metricHeaderEntries": [{
"name": "ga:pageviews",
"type": "INTEGER"
}, {
"name": "ga:exits",
"type": "INTEGER"
}]
}
},
"data": {
"rows": [{
"dimensions": ["(entrance)", "/page/turtles/"],
"metrics": [{
"values": ["13", "9"]
}]
}, {
"dimensions": ["(entrance)", "/page/dogs/"],
"metrics": [{
"values": ["6", "5"]
}]
}, {
"dimensions": ["(entrance)", "/"],
"metrics": [{
"values": ["1", "1"]
}]
}, {
"dimensions": ["(entrance)", "/page/cats/"],
"metrics": [{
"values": ["1", "1"]
}]
}, {
"dimensions": ["/turtles/turtle1.html", "/page/turtles/"],
"metrics": [{
"values": ["3", "3"]
}]
}, {
"dimensions": ["/page/turtles/", "/turtles/turtle1.html"],
"metrics": [{
"values": ["3", "0"]
}]
}, {
"dimensions": ["/page/turtles/", "/turtles/tortise.html"],
"metrics": [{
"values": ["1", "1"]
}]
}, {
"dimensions": ["/page/dogs/", "/dogs/dog_photo2.html"],
"metrics": [{
"values": ["1", "0"]
}]
}, {
"dimensions": ["/dogs/dog_photo2.html", "/page/dogs/"],
"metrics": [{
"values": ["1", "1"]
}]
}],
"totals": [{
"values": ["30", "21"]
}],
"rowCount": 9,
"minimums": [{
"values": ["0", "0"]
}],
"maximums": [{
"values": ["13", "9"]
}]
}
}, {
"columnHeader": {
"dimensions": ["ga:previousPagePath", "ga:pagePath"],
"metricHeader": {
"metricHeaderEntries": [{
"name": "ga:pageviews",
"type": "INTEGER"
}, {
"name": "ga:exits",
"type": "INTEGER"
}]
}
},
"data": {
"rows": [{
"dimensions": ["(entrance)", "/page/turtles/"],
"metrics": [{
"values": ["13", "9"]
}]
}, {
"dimensions": ["(entrance)", "/page/dogs/"],
"metrics": [{
"values": ["6", "5"]
}]
}, {
"dimensions": ["(entrance)", "/"],
"metrics": [{
"values": ["1", "1"]
}]
}, {
"dimensions": ["(entrance)", "/page/cats/"],
"metrics": [{
"values": ["1", "1"]
}]
}, {
"dimensions": ["/turtles/turtle1.html", "/page/turtles/"],
"metrics": [{
"values": ["3", "3"]
}]
}, {
"dimensions": ["/page/turtles/", "/turtles/turtle1.html"],
"metrics": [{
"values": ["3", "0"]
}]
}, {
"dimensions": ["/page/turtles/", "/turtles/tortise.html"],
"metrics": [{
"values": ["1", "1"]
}]
}, {
"dimensions": ["/page/dogs/", "/dogs/dog_photo2.html"],
"metrics": [{
"values": ["1", "0"]
}]
}, {
"dimensions": ["/dogs/dog_photo2.html", "/page/dogs/"],
"metrics": [{
"values": ["1", "1"]
}]
}],
"totals": [{
"values": ["30", "21"]
}],
"rowCount": 9,
"minimums": [{
"values": ["0", "0"]
}],
"maximums": [{
"values": ["13", "9"]
}]
}
}]}
================================================
FILE: experiments/guess-static-sites/test/fixtures/server.js
================================================
// Simple static server for serving a page for testing front-end script
const express = require('express')
const cors = require('cors')
const path = require('path')
const fs = require('fs')
const app = express()
const PORT = 8080
app.use(cors())
app.get('/test.html', function (req, res) {
res.sendFile(path.join(__dirname + '/test.html'))
})
app.get('/predictiveFetching.js', async (req, res) => {
fs.readFile(path.join(__dirname + '/../../predictiveFetching.js'), 'utf8', (err, data) => {
const response = data.replace(/http:\/\/YOUR_SERVER_ENDPOINT\//g, 'http://localhost:3000/')
res.send(response)
})
})
app.listen(PORT, function () {
console.log('Test web server listening on port ' + PORT)
})
================================================
FILE: experiments/guess-static-sites/test/fixtures/test.html
================================================
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Next Page Predictor</title>
</head>
<body>
Page for testing predictive fetching.
<script src="/predictiveFetching.js"></script>
</body>
</html>
================================================
FILE: experiments/guess-static-sites/test/gaClientTests.js
================================================
// To run tests: $ npm run generate-predictions-tests
// mongod should be running
const chai = require('chai')
const expect = chai.expect
const mongoose = require('mongoose')
const config = require('../config')
const parser = require('../src/parser')
const fakeResponse = require('./fixtures/gaResponse.json')
const Prediction = require('../src/models/prediction')
describe('#SaveReports', function () {
beforeEach(async () => {
await mongoose.connect(config.db.mongoURL)
await mongoose.connection.db.dropDatabase()
})
afterEach(async () => {
mongoose.disconnect()
})
it('should save API response as predictions', async () => {
await parser.saveReports(fakeResponse['testData'])
const predictions = await Prediction.find({})
expect(await predictions.length).to.equal(4)
const prediction1 = predictions.find((p) => { return p.pagePath === '/page/turtles/' })
expect(prediction1.nextPageCertainty).to.equal(0.1875)
expect(prediction1.nextPagePath).to.equal('/turtles/turtle1.html')
const prediction2 = predictions.find((p) => { return p.pagePath === '/page/dogs/' })
expect(prediction2.nextPageCertainty).to.equal(0.14285714285714285)
expect(prediction2.nextPagePath).to.equal('/dogs/dog_photo2.html')
})
})
================================================
FILE: experiments/guess-static-sites/test/serverTests.js
================================================
// $ npm run server-tests
const request = require('supertest')
const express = require('express')
const chai = require('chai')
const mongoose = require('mongoose')
const Prediction = require('../src/models/prediction')
const PageView = require('../src/models/pageView')
const expect = chai.expect
const app = require('../server.js')
const config = require('../config')
describe('POST /', function () {
beforeEach(async () => {
await mongoose.connect(config.db.mongoURL)
await mongoose.connection.db.dropDatabase()
})
afterEach(async () => {
mongoose.disconnect()
})
describe('response', function () {
describe('when a prediction for that page exists in the database', async () => {
const makeRequest = function () {
return request(app)
.post('/')
.send({
pagePath: '/page/1',
userFlow: ['pf_123', 'pf_456'],
clientInfo: {
connectionType: '4g',
platform: 'MacIntel',
language: 'en-US'
}
})
}
beforeEach(async () => {
await Prediction.create({
pagePath: '/page/1',
nextPagePath: '/page/2',
nextPageCertainty: 0.75
})
})
it('should create a new PageView record', async () => {
await makeRequest()
const pageView = await PageView.findOne({})
expect(pageView.pagePath).to.equal('/page/1')
expect(pageView.clientInfo.connectionType).to.equal('4g')
expect(pageView.clientInfo.platform).to.equal('MacIntel')
expect(pageView.clientInfo.language).to.equal('en-US')
expect(pageView.userFlow[0]).to.equal('pf_123')
expect(pageView.userFlow[1]).to.equal('pf_456')
expect(pageView.prefetchPath).to.equal('/page/2')
expect(pageView.actualNextPagePath).to.equal(undefined)
})
it('should have the correct response body', async () => {
const response = await makeRequest()
const pageView = await PageView.findOne({pagePath: '/page/1'})
expect(response.body['pageViewId']).to.equal(pageView._id.toString())
expect(response.body['prefetchPath']).to.equal('/page/2')
expect(200)
})
})
describe('when a prediction for that page does not exist in the database', async () => {
const makeRequest = function () {
return request(app)
.post('/')
.send({
pagePath: '/no/matches',
userFlow: [],
clientInfo: {
connectionType: '4g'
}
})
}
it('should create a new PageView record', async () => {
await makeRequest()
const pageView = await PageView.findOne({})
expect(pageView.prefetchPath).to.equal('')
expect(pageView.pagePath).to.equal('/no/matches')
expect(pageView.clientInfo.connectionType).to.equal('4g')
expect(pageView.userFlow.length).to.equal(0)
expect(pageView.actualNextPagePath).to.equal(undefined)
})
it('should have the correct response body', async () => {
const response = await makeRequest()
const pageView = await PageView.findOne({pagePath: '/no/matches'})
expect(response.body['pageViewId']).to.equal(pageView._id.toString())
expect(response.body['prefetchPath']).to.equal('')
expect(200)
})
})
})
describe('using connectionType to determine whether page should be prefetched', async () => {
const makeRequestOnConnectionType = function (connectionType) {
return request(app)
.post('/')
.send({
pagePath: '/page/1',
userFlow: ['pf_123', 'pf_456'],
clientInfo: {
connectionType: connectionType,
platform: 'MacIntel',
language: 'en-US'
}
})
}
beforeEach(async () => {
await Prediction.create({
pagePath: '/page/1',
nextPagePath: '/page/2',
nextPageCertainty: 0.75
})
})
describe('when client is on a fast connection', async () => {
it('should respond with a page to prefetch', async () => {
const response = await makeRequestOnConnectionType('4g')
expect(response.body['prefetchPath']).to.equal('/page/2')
expect(200)
})
})
describe('when client is on a slow connection', async () => {
it('should not respond with a page to prefetch', async () => {
const response = await makeRequestOnConnectionType('slow-2g')
expect(response.body['prefetchPath']).to.equal('')
expect(200)
})
})
describe('when client is on an unknown connection', async () => {
it('should not respond with a page to prefetch', async () => {
const response = await makeRequestOnConnectionType('foobar')
expect(response.body['prefetchPath']).to.equal('')
expect(200)
})
})
describe('when client is using Save-Data header', async () => {
it('should not respond with a page to prefetch', async () => {
const response = await request(app)
.post('/')
.set('Save-Data', true)
.send({
pagePath: '/page/1',
userFlow: ['pf_123', 'pf_456'],
clientInfo: {
connectionType: '4g',
platform: 'MacIntel',
language: 'en-US'
}
})
expect(response.body['prefetchPath']).to.equal('')
expect(200)
})
})
})
describe('previous pageviews', async () => {
it('updates existing previous page record', async () => {
const existingPageView = await PageView.create({
pagePath: '/old.html',
clientInfo: {
connectionType: '3g'
},
userFlow: [],
prefetchPath: '',
actualNextPagePath: ''
})
await request(app)
.post('/')
.send({
pagePath: '/this/page',
userFlow: ['pf_5ad567927c8db21eec4ae909=1523890000', 'pf_' + existingPageView._id + '=1523891234'],
clientInfo: {
connectionType: '4g'
}
})
const updatedRecord = await PageView.findById(existingPageView._id)
expect(updatedRecord.actualNextPagePath).to.equal('/this/page')
})
})
})
================================================
FILE: infra/e2e.ts
================================================
import { execSync } from 'child_process';
import { readFileSync } from 'fs';
const enterTest = 'cd packages/guess-webpack/test/fixtures/angular';
console.log(execSync(`${enterTest} && npm i`).toString());
console.log(execSync(
`${enterTest} && ./node_modules/.bin/ng build --extra-webpack-config webpack.extra.js`
).toString());
// Prefetching instruction for baz
const fooModule = readFileSync('packages/guess-webpack/test/fixtures/angular/dist/angular/foo-foo-module.js').toString();
if (fooModule.indexOf(`__GUESS__.p([0.6,'baz-baz-module.js'],[0.4,'qux-qux-module.js'])`) < 0) {
console.error('Problem with the ordering, or cannot find prefetching instructions');
process.exit(1);
}
// Proper filtering of the instructions
const quxModule = readFileSync('packages/guess-webpack/test/fixtures/angular/dist/angular/qux-qux-module.js').toString();
if (quxModule.indexOf(`__GUESS__.p([0.99,'foo-foo-module.js'])`) < 0) {
console.error('Problem with filtering prefetching instructions');
process.exit(1);
}
// No prefetching instructions
const bazModule = readFileSync('packages/guess-webpack/test/fixtures/angular/dist/angular/baz-baz-module.js').toString();
if (bazModule.indexOf('__GUESS__') >= 0) {
console.error('Found prefetching instructions in bundle with no neighbors');
process.exit(1);
}
// No runtime
const mainModule = readFileSync('packages/guess-webpack/test/fixtures/angular/dist/angular/main.js').toString();
if (mainModule.indexOf('__GUESS__.p(') < 0 || mainModule.indexOf('__GUESS__.p=') < 0) {
console.error('Unable to find runtime or initial prefetching instruction');
process.exit(1);
}
// No base
if (mainModule.indexOf('"http://localhost:1337"') < 0) {
console.error('Unable to find the base path');
process.exit(1);
}
// Prod build should work
console.log(execSync(
`${enterTest} && ./node_modules/.bin/ng build --prod --extra-webpack-config webpack.extra.js`
).toString());
================================================
FILE: infra/install.ts
================================================
import { join } from 'path';
import { execSync } from 'child_process';
const PackagesDir = join(process.cwd(), 'packages');
console.log(
execSync(
`cd ${join(PackagesDir, 'guess-parser', 'test', 'fixtures', 'angular')} && npm i`
).toString()
);
================================================
FILE: infra/pretest.ts
================================================
import { readdirSync } from 'fs';
import { join } from 'path';
import { execSync } from 'child_process';
const cwd = process.cwd();
const base = join(cwd, 'packages', 'guess-webpack', 'test', 'fixtures');
readdirSync(base).forEach(dir => {
if (dir === '.' || dir === '..') {
return;
}
execSync(`cd ${join(base, dir)} && rm -rf dist && npm i && npm run build`);
});
================================================
FILE: infra/test.ts
================================================
import { join } from 'path';
import { spawn } from 'child_process';
import chalk from 'chalk';
const StaticServer = require('static-server');
const port = 5122;
const setupMockServers = () =>
new Promise(resolve => {
const server = new StaticServer({
rootPath: join(process.cwd(), 'packages', 'guess-webpack', 'test', 'fixtures'),
port
});
server.start(() => {
console.log(chalk.yellow('Test server started on port', server.port));
resolve(server);
});
});
async function main() {
await setupMockServers();
const options = process.argv.filter(a => a === '--watch');
const jest = spawn(`${process.cwd()}/node_modules/.bin/jest`, options, { stdio: 'inherit' });
return new Promise<number>(resolve => {
jest.on('exit', code => resolve(code));
jest.on('close', code => resolve(code));
});
}
main().then(code => process.exit(code));
================================================
FILE: jest-puppeteer.config.js
================================================
module.exports = {
launch: {
args: ['--no-sandbox', '--disable-setuid-sandbox'],
headless: true
}
};
================================================
FILE: jest.config.js
================================================
module.exports = {
transform: {
'^.+\\.tsx?$': 'ts-jest'
},
testRegex: '(/test/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$',
testPathIgnorePatterns: [
'<rootDir>/packages/guess-parser/test/fixtures',
'<rootDir>/infra/test.ts',
'<rootDir>/experiments/guess-static-sites/test',
'<rootDir>/packages/guess-webpack/test/fixtures'
],
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
preset: '<rootDir>/node_modules/jest-puppeteer',
globals: {
window: {}
}
};
================================================
FILE: lerna.json
================================================
{
"lerna": "2.11.0",
"packages": [
"packages/*"
],
"version": "0.4.22"
}
================================================
FILE: package.json
================================================
{
"name": "guess",
"version": "0.0.0",
"description": "Smart bundling",
"main": "index.js",
"scripts": {
"bootstrap": "npm i && lerna bootstrap",
"build": "lerna run build",
"publish": "lerna publish",
"e2e": "ts-node infra/e2e.ts",
"test": "ts-node infra/test.ts --watch",
"pretest": "ts-node infra/pretest.ts",
"test:ci": "ts-node infra/pretest.ts && ts-node infra/test.ts",
"postinstall": "ts-node infra/install.ts"
},
"repository": {
"type": "git",
"url": "git+https://github.com/guess-js/guess.git"
},
"keywords": [
"bundling",
"webpack",
"ml",
"ai",
"analytics",
"recommendation"
],
"author": "Minko Gechev <mgechev@gmail.com>",
"license": "MIT",
"bugs": {
"url": "https://github.com/guess-js/guess/issues"
},
"homepage": "https://github.com/guess-js/guess#readme",
"devDependencies": {
"@types/jest": "25.2.1",
"@types/meow": "5.0.0",
"@types/node": "9.6.60",
"blund": "1.0.0",
"chalk": "2.4.2",
"copy-webpack-plugin": "5.1.2",
"jest": "26.6.3",
"jest-puppeteer": "4.4.0",
"lerna": "4.0.0",
"meow": "10.1.2",
"prompt-confirm": "2.0.4",
"puppeteer": "7.1.0",
"raw-loader": "4.0.1",
"static-server": "2.2.1",
"ts-jest": "26.5.1",
"ts-loader": "7.0.0",
"ts-node": "8.8.2",
"tslint": "6.1.1",
"typescript": "4.5.4",
"webpack": "4.46.0",
"webpack-cli": "4.5.0"
},
"dependencies": {
"flat-cache": "^3.0.0",
"googleapis": "^67.0.0"
}
}
================================================
FILE: packages/common/interfaces.ts
================================================
export interface Neighbors {
[key: string]: number;
}
export interface Graph {
[key: string]: Neighbors;
}
export interface Module {
modulePath: string;
parentModulePath: string;
}
export interface RoutingModule {
path: string;
modulePath: string;
parentModulePath: string | null;
lazy: boolean;
redirectTo?: string;
}
export interface Connection {
from: string;
weight: number;
to: string;
}
export interface Period {
startDate: Date;
endDate: Date;
}
export enum ProjectType {
AngularCLI = 'angular-cli',
CreateReactApp = 'create-react-app',
PreactCLI = 'preact-cli',
Gatsby = 'gatsby',
CreateReactAppTypeScript = 'create-react-app-typescript'
}
export interface ProjectLayout {
typescript?: string;
tsconfigPath?: string;
sourceDir?: string;
}
export interface ProjectMetadata {
type: ProjectType;
version: string;
details?: ProjectLayout;
}
================================================
FILE: packages/common/logger.ts
================================================
export enum LogLevel {
DEBUG,
INFO,
WARN,
ERROR,
OFF
}
export class Logger {
constructor(private level = LogLevel.INFO) {}
setLevel(newLevel: LogLevel) {
this.level = newLevel;
}
debug(...msg: any[]) {
this.print(LogLevel.DEBUG, 'DEBUG', msg);
}
info(...msg: any[]) {
this.print(LogLevel.INFO, 'INFO', msg);
}
warn(...msg: any[]) {
this.print(LogLevel.WARN, 'WARN', msg);
}
error(...msg: any[]) {
this.print(LogLevel.ERROR, 'ERROR', msg);
}
private print(level: LogLevel, label: string, msg: any[]) {
if (level >= this.level) {
switch (level) {
case LogLevel.DEBUG:
console.debug(this.prettify(label), ...msg);
break;
case LogLevel.INFO:
console.info(this.prettify(label), ...msg);
break;
case LogLevel.WARN:
console.warn(this.prettify(label), ...msg);
break;
case LogLevel.ERROR:
console.error(this.prettify(label), ...msg);
break;
default:
console.log(this.prettify(label), ...msg);
break;
}
}
}
private prettify(label: string) {
return `${label}::${Date.now()}::`;
}
}
================================================
FILE: packages/guess-ga/.npmignore
================================================
node_modules
src
test
index.ts
tsconfig.json
webpack.config.js
================================================
FILE: packages/guess-ga/README.md
================================================
# GA
Fetches data from Google analytics.
## Setup
### Create Your Credentials
#### i) Create a Service Account
1. Go to the [Credentials](https://console.developers.google.com/apis/credentials) page in the Google APIs console.
2. If you don't have an existing Google Cloud project, click "Create" to create a new project. Otherwise, use the dropdown in the upper left corner to select the existing project that you'd like to use.
3. Select "Service Account key" from the "Create credentials" dropdown.
4. Fill out the form for creating a service account key:
- **Service account dropdown:** Select "New Service Account".
- **Service account name:** Give your service account a name.
- **Role:** Select "Service Account User" ("Service Accounts" > "Service Account User").
- **Service account ID:** This field will automatically be pre-filled, but you can change this if you would like.
- **Key type:** Select JSON key.
*This should start a download of the credentials file*
### Configure Google Analytics
#### i) Add service account to Google Analytics
The service account that you just created needs to be added as a user to your Google analytics account.
1. Login to your [Google Analytics](https://analytics.google.com/analytics/web/) account.
2. Add a new user. (Admin > User Management > + > Add New Users)
- **Email Address** The email address of the service account you created. It should look something like this: example@example-project-123456.iam.gserviceaccount.com.
- **Permissions:** Select "Read & Analyze."
*Note: A service account can only be associated with one Google Analytics account at a time. Thus, if you want to use predictive fetching on multiple sites, you'll need to create a separate service account for each.*
#### ii) Enable the Google Analytics Reporting API
You can enable this [here.](https://console.developers.google.com/flows/enableapi?apiid=analyticsreporting.googleapis.com&credential=client_key)
### Set up credentials
To use the credentials in your project, copy it your project,
and make sure to add it or its folder to gitignore.
You can also opt to take `client_email` and `private_key` from the credentials file, and add them to env.
### Get your view ID
To find your view ID, go to [Google Analytics](https://analytics.google.com/analytics/web/).
Click the accounts dropdown (it's located in the upper lefthand corner of the screen, right next to the Google Analytics logo). The dialog that opens will have three columns: Analytics Accounts, Properties & Apps, & Views. The far right column ("Views") will contain the View ID for your site.
You can opt to save this in an .env file if you do not want to share this information.
## Usage
```bash
npm i guess-ga
```
Combined with `guess-parser` you can aggregate the route information and map it to your application's parametrized routes:
```ts
const { fetch } = require('guess-ga');
const { parseRoutes, ProjectType } = require('guess-parser');
const { JWT } = require('google-auth-library');
const { writeFileSync } = require('fs');
const credentials = require('./secret/credentials.json');
const auth = new JWT(
credentials.client_email,
null,
credentials.private_key,
['https://www.googleapis.com/auth/analytics.readonly']
);
const viewId = '000000000';
const applicationRoutes = parseRoutes('tsconfig.app.json', ProjectType.Angular);
fetch({
auth,
viewId,
period: {
startDate: new Date('2018-1-1'),
endDate: new Date(Date.now())
},
formatter: r => r.replace('/app', ''),
routes: applicationRoutes.map(f => f.path)
}).then(g => {
writeFileSync('data.json', JSON.stringify(g, null, 2));
});
```
For more details visit [https://github.com/guess-js/guess](https://github.com/guess-js/guess).
## License
MIT
================================================
FILE: packages/guess-ga/index.ts
================================================
export * from './src/ga';
================================================
FILE: packages/guess-ga/package.json
================================================
{
"name": "guess-ga",
"version": "0.4.20",
"description": "Fetch structured data from Google Analytics",
"main": "dist/guess-ga/index.js",
"types": "dist/guess-ga/index.d.ts",
"repository": {
"type": "git",
"url": "git+https://github.com/guess-js/guess"
},
"keywords": [
"bundling",
"webpack",
"ml",
"ai",
"google analytics",
"ga",
"analytics",
"recommendation"
],
"author": "Minko Gechev <mgechev@gmail.com>",
"license": "MIT",
"bugs": {
"url": "https://github.com/guess-js/guess/issues"
},
"homepage": "https://github.com/guess-js/guess#readme",
"dependencies": {
"googleapis": "^67.0.0"
},
"devDependencies": {
"ts-loader": "7.0.0",
"typescript": "4.5.4"
},
"files": [
"dist"
],
"jest": {
"transform": {
"^.+\\.tsx?$": "ts-jest"
},
"testRegex": "(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$",
"moduleFileExtensions": [
"ts",
"tsx",
"js",
"jsx",
"json",
"node"
]
},
"scripts": {
"build": "webpack"
}
}
================================================
FILE: packages/guess-ga/src/client.ts
================================================
import { Period } from '../../common/interfaces';
interface PageConfig {
pageToken: number | undefined;
pageSize: number;
}
interface AnalyticsResult {
report: any;
nextPage: number;
}
const formatNumber = (n: number) => (n.toString().length === 1 ? '0' + n : n);
const formatDate = (d: Date) => `${d.getFullYear()}-${formatNumber(d.getMonth() + 1)}-${formatNumber(d.getDate())}`;
function requestBuilder(jwtClient: any, viewId: string, pageConfig: PageConfig, period: Period, expression: string) {
return {
auth: jwtClient,
resource: {
reportRequests: {
pageSize: pageConfig.pageSize,
pageToken: pageConfig.pageToken,
viewId,
dateRanges: [
{
startDate: formatDate(period.startDate),
endDate: formatDate(period.endDate)
}
],
dimensions: [{ name: 'ga:previousPagePath' }, { name: 'ga:pagePath' }],
metrics: [{ expression }],
orderBys: [{ fieldName: expression, sortOrder: 'DESCENDING' }]
}
}
};
}
async function fetchReport(
client: any,
jwtClient: any,
viewId: string,
pageConfig: PageConfig,
period: Period,
expression: string
) {
return new Promise<AnalyticsResult>((resolve, reject) => {
client.reports.batchGet(requestBuilder(jwtClient, viewId, pageConfig, period, expression), function(
err: any,
response: any
) {
if (err) {
reject(err);
return;
}
const nextPage = response.data.reports[0].nextPageToken;
const report = response.data.reports[0];
resolve({
report,
nextPage
});
});
});
}
if (typeof (Symbol as any).asyncIterator === 'undefined') {
(Symbol as any).asyncIterator = Symbol.asyncIterator || Symbol('asyncIterator');
}
export type GaResult = any;
export interface ClientResult {
error?: GaResult;
report?: any;
}
export function getClient(jwtClient: any, pageSize: number, viewId: string, period: Period, expression: string) {
const { google } = require('googleapis');
const client = google.analyticsreporting('v4');
const pageConfig: PageConfig = {
pageSize,
pageToken: undefined
};
async function* reportGenerator(): AsyncIterableIterator<ClientResult> {
while (true) {
const clientResult: ClientResult = {};
try {
const result = await fetchReport(client, jwtClient, viewId, pageConfig, period, expression);
clientResult.report = result.report;
pageConfig.pageToken = result.nextPage;
} catch (e) {
clientResult.error = e;
}
yield clientResult;
if (!pageConfig.pageToken) {
break;
}
}
}
return reportGenerator;
}
================================================
FILE: packages/guess-ga/src/ga.ts
================================================
import { getClient } from './client';
import { normalize } from './normalize';
import { Graph, Period } from '../../common/interfaces';
const PageSize = 10000;
const id = (r: string) => r;
const DefaultExpression = 'ga:pageviews';
export interface FetchConfig {
auth: any;
viewId: string;
period: Period;
formatter?: (route: string) => string;
routes?: string[];
expression?: string;
}
export async function fetch(config: FetchConfig) {
const client = getClient(config.auth, PageSize, config.viewId, config.period, config.expression || DefaultExpression);
const graph: Graph = {};
for await (const val of client()) {
if (val.error) {
throw val.error;
}
const result = val.report;
normalize(result.data, config.formatter || id, config.routes || []).forEach((n: any) => {
const r = graph[n.from] || {};
r[n.to] = n.weight + (r[n.to] || 0);
graph[n.from] = r;
});
}
return graph;
}
================================================
FILE: packages/guess-ga/src/normalize.ts
================================================
import { Connection } from '../../common/interfaces';
export const matchRoute = (route: string, declaration: string): boolean => {
const routeParts: string[] = route.split('/');
const declarationParts: string[] = declaration.split('/');
if (routeParts.length !== declarationParts.length) {
return false;
} else {
return declarationParts.reduce((a: boolean, p: string, i: number) => {
if (p.startsWith(':')) {
return a;
}
return a && p === routeParts[i];
}, true);
}
};
const findRoute = (d: string[], r: string) =>
d.filter(def => def.indexOf(':') < 0).find(c => matchRoute(r, c)) || d.find(c => matchRoute(r, c)) || r;
const processRoute = (declarations: string[], route: string) => findRoute(declarations, route.split('?')[0]);
export const normalize = (data: any, formatter: (s: string) => string, declarations: string[]) => {
return (data.rows || [])
.map((r: any) => {
return {
from: processRoute(declarations, formatter(r.dimensions[0])),
to: processRoute(declarations, formatter(r.dimensions[1])),
weight: parseInt(r.metrics[0].values[0], 10)
};
})
.filter((node: Connection) => node.from !== '(entrance)' && node.from !== node.to);
};
================================================
FILE: packages/guess-ga/test/normalize.spec.ts
================================================
import { matchRoute, normalize } from '../src/normalize';
const GAData = {
rows: [
{
dimensions: ['/a', '/b'],
metrics: [
{
values: [1]
}
]
},
{
dimensions: ['/a/4', '/c'],
metrics: [
{
values: [1]
}
]
},
{
dimensions: ['/a/3', '/c'],
metrics: [
{
values: [1]
}
]
},
{
dimensions: ['/a', '/a/3'],
metrics: [
{
values: [1]
}
]
}
]
};
describe('matchRoute', () => {
it('should work simple routes', () => {
expect(matchRoute('/', '/')).toBeTruthy();
expect(matchRoute('/foo', '/foo')).toBeTruthy();
expect(matchRoute('/bar/baz', '/bar/baz')).toBeTruthy();
});
it('should work with parameters', () => {
expect(matchRoute('/bar', '/:random')).toBeTruthy();
expect(matchRoute('/foo/1', '/foo/:id')).toBeTruthy();
});
it('should fail with trailing slash', () => {
expect(matchRoute('/foo', '/foo/')).toBeFalsy();
});
});
describe('normalize', () => {
it('should work without a trailing slash', () => {
const normalized = normalize(GAData, a => a, ['/a/', '/b', '/a/:id', 'c']);
expect(normalized).toEqual([
{ from: '/a', to: '/b', weight: 1 },
{ from: '/a/:id', to: '/c', weight: 1 },
{ from: '/a/:id', to: '/c', weight: 1 },
{ from: '/a', to: '/a/:id', weight: 1 }
]);
});
});
================================================
FILE: packages/guess-ga/tsconfig.json
================================================
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"sourceMap": true,
"declaration": true,
"experimentalDecorators": true,
"strict": true,
"outDir": "dist",
"downlevelIteration": true,
"lib": ["es2015", "esnext", "dom"]
},
"files": ["index.ts"]
}
================================================
FILE: packages/guess-ga/webpack.config.js
================================================
module.exports = {
mode: 'production',
entry: './index.ts',
target: 'node',
output: {
filename: './guess-ga/index.js',
libraryTarget: 'umd'
},
externals: [/^(@|\w{3}(?<!\w:\\)).*$/i],
resolve: {
// Add `.ts` and `.tsx` as a resolvable extension.
extensions: ['.ts', '.js', '.json']
},
module: {
rules: [
// all files with a `.ts` or `.tsx` extension will be handled by `ts-loader`
{ test: /\.tsx?$/, loader: 'ts-loader' }
]
}
};
================================================
FILE: packages/guess-parser/.npmignore
================================================
node_modules
src
test
index.ts
tsconfig.json
webpack.config.js
================================================
FILE: packages/guess-parser/README.md
================================================
# guess-parser
This module is used for route extraction by the `GuessPlugin`. The module exports several functions:
## Usage
```bash
npm i guess-parser --save-dev
```
## API
* `detect(path: string)` - Detects the project type and returns metadata. For the currently supported projects see the `ProjectMetadata` interface.
* `parseRoutes(path: string)` - Extracts the routes of the application in `path`. Internally uses the `detect` function.
* `parseAngularRoutes(tsconfig: string)` - Extracts the routes of an Angular application. As arguments the function accepts path to the `tsconfig.json` file of the project.
* `parseReactJSXRoutes(path: string)` - Extracts the routes from React JSX project. See the supported syntax below.
* `parseReactTSXRoutes(tsconfig: string)` - Extracts the routes from React TypeScript projects which uses JSX by `tsconfig.json` file. See the supported syntax below.
```ts
export interface ProjectMetadata {
type: ProjectType;
version: string;
details?: ProjectLayout;
}
export enum ProjectType {
AngularCLI = 'angular-cli',
CreateReactApp = 'create-react-app',
Gatsby = 'gatsby',
CreateReactAppTypeScript = 'create-react-app-typescript'
}
export interface ProjectLayout {
typescript?: string;
tsconfigPath?: string;
sourceDir?: string;
}
```
## Supported Syntax
### Angular
Because of the produced summaries by the Angular compiler the Angular parser supports most Angular CLI applications as well as most starters.
### React
Because of the dynamic nature of React and lack of standard route definition syntax, only applications using the following convention can be successfully parsed:
```jsx
<Router history={history}>
<div className="App">
<Link to="/intro">Intro</Link>
<Link to="/main">Main</Link>
<div>
<Switch>
<Redirect exact={true} from="/" to="/intro" />
<Route path="/intro" component={AsyncComponent(() => import('./intro/Intro'))} />
<Route path="/main" component={Main} />
</Switch>
</div>
</div>
</Router>
```
Currently, there are several important conventions:
* Support only for JSX syntax
* Support only for `react-router`-like syntax
* The path attribute of the `<Route/>` element must have value of type string literal.
* The lazy-loaded components should have dynamic import with the following structure of the AST:
* `CallExpression` (e.g. `AsyncComponent`) with a single argument
* The type of the argument should be an `ArrowFunction`
* The arrow function should have an expression as body (e.g. `CallExpression`)
* To the `CallExpression` should be passed a `StringLiteral` which points to the lazy-loaded module
**Contributions aiming to extend the supported syntax are very welcome!**
## License
MIT
================================================
FILE: packages/guess-parser/index.ts
================================================
export { parseRoutes } from './src/parser';
export { detect } from './src/detector';
export { parseRoutes as parseAngularRoutes } from './src/angular';
export * from './src/react';
export * from './src/preact';
================================================
FILE: packages/guess-parser/package.json
================================================
{
"name": "guess-parser",
"version": "0.4.22",
"description": "Finds the route declarations in your application.",
"main": "dist/guess-parser/index.js",
"types": "dist/guess-parser/index.d.ts",
"repository": {
"type": "git",
"url": "git+https://github.com/guess-js/guess"
},
"keywords": [
"bundling",
"webpack",
"ml",
"ai",
"analytics",
"recommendation"
],
"author": "Minko Gechev <mgechev@gmail.com>",
"license": "MIT",
"bugs": {
"url": "https://github.com/guess-js/guess/issues"
},
"homepage": "https://github.com/guess-js/guess#readme",
"dependencies": {
"ts-evaluator": "0.1.0"
},
"devDependencies": {
"ts-loader": "7.0.0"
},
"peerDependencies": {
"typescript": ">=3.7.5"
},
"files": [
"dist"
],
"jest": {
"transform": {
"^.+\\.tsx?$": "ts-jest"
},
"testRegex": "(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$",
"moduleFileExtensions": [
"ts",
"tsx",
"js",
"jsx",
"json",
"node"
]
},
"scripts": {
"build": "webpack"
}
}
================================================
FILE: packages/guess-parser/src/angular/index.ts
================================================
export * from './route-parser';
================================================
FILE: packages/guess-parser/src/angular/modules.ts
================================================
import * as ts from 'typescript';
import { resolve, join, dirname, sep } from 'path';
import { existsSync } from 'fs';
import { LazyRoute, Route, readLoadChildren } from './routes';
import { RoutingModule } from '../../../common/interfaces';
interface RoutesDeclaration {
lazyRoutes: LazyRoute[];
eagerRoutes: Route[];
}
export interface Registry {
[path: string]: RoutesDeclaration;
}
export const findRootModule = (registry: Registry): string => {
const childModules = new Set<string>();
const traverseRoute = (route: Route) => {
if ((route as LazyRoute).module) {
childModules.add((route as LazyRoute).module);
}
route.children.forEach(traverseRoute);
};
const allModulePaths = Object.keys(registry);
allModulePaths.forEach(path => {
const declaration = registry[path];
// It's possible if the declaration does not exist
// See https://github.com/guess-js/guess/issues/311
if (declaration) {
declaration.eagerRoutes.forEach(traverseRoute);
declaration.lazyRoutes.forEach(traverseRoute);
}
});
const roots = allModulePaths.filter(m => !childModules.has(m));
if (roots.length > 1) {
throw new Error('Multiple root routing modules found ' + roots.join(', '));
}
return roots[0];
};
export const collectRoutingModules = (
rootFile: string,
registry: Registry,
result: RoutingModule[],
parentFilePath: string = rootFile,
currentRoutePath: string = '',
existing = new Set<string>()
) => {
const declaration = registry[rootFile];
// It's possible if the declaration does not exist
// See https://github.com/guess-js/guess/issues/311
if (!declaration) {
return;
}
const process = (r: Route, routePath = currentRoutePath) => {
if ((r as LazyRoute).module) {
// tslint:disable-next-line: no-use-before-declare
return processLazyRoute(r as LazyRoute, routePath);
}
// tslint:disable-next-line: no-use-before-declare
return processRoute(r, routePath);
};
const processRoute = (r: Route, routePath = currentRoutePath) => {
const path = (routePath + '/' + r.path).replace(/\/$/, '');
r.children.forEach(route => process(route, path));
if (!existing.has(path)) {
const routingModule: RoutingModule = {
path,
lazy: parentFilePath !== rootFile && r.redirectTo === undefined,
modulePath: rootFile,
parentModulePath: parentFilePath,
};
if (r.redirectTo !== undefined) {
routingModule.redirectTo = r.redirectTo;
}
result.push(routingModule);
existing.add(path);
}
};
const processLazyRoute = (r: LazyRoute, routePath = currentRoutePath) => {
const path = (routePath + '/' + r.path).replace(/\/$/, '');
r.children.forEach(route => process(route, path));
collectRoutingModules(r.module, registry, result, rootFile, path);
};
declaration.eagerRoutes.forEach(r => processRoute(r));
declaration.lazyRoutes.forEach(r => processLazyRoute(r));
};
export const findMainModule = (program: ts.Program) => {
const tryFindMainModule = (n: ts.Node, sf: ts.SourceFile) => {
if (
n.kind === ts.SyntaxKind.Identifier &&
(n as ts.Identifier).text === 'bootstrapModule'
) {
const propAccess = (n as ts.Identifier).parent;
if (
!propAccess ||
propAccess.kind !== ts.SyntaxKind.PropertyAccessExpression
) {
return null;
}
const tempExpr = propAccess.parent;
if (!tempExpr || tempExpr.kind !== ts.SyntaxKind.CallExpression) {
return null;
}
const expr = tempExpr as ts.CallExpression;
const module = expr.arguments[0];
const tc = program.getTypeChecker();
const symbol = tc.getTypeAtLocation(module).getSymbol();
if (!symbol) {
return null;
}
const decl = symbol.getDeclarations();
if (!decl) {
return null;
}
return resolve(decl[0].getSourceFile().fileName);
}
let mainPath: null | string = null;
n.forEachChild(c => {
if (mainPath) {
return mainPath;
}
mainPath = tryFindMainModule(c, sf);
});
return mainPath;
};
return program.getSourceFiles().reduce((a, sf) => {
if (a) {
return a;
}
let mainPath: null | string = null;
sf.forEachChild(n => {
if (mainPath) {
return;
}
mainPath = tryFindMainModule(n, sf);
});
return mainPath;
}, null);
};
const isImportDeclaration = (node: ts.Node): node is ts.ImportDeclaration => {
return node.kind === ts.SyntaxKind.ImportDeclaration;
};
const isReExportDeclaration = (node: ts.Node): node is ts.ExportDeclaration => {
return (node.kind === ts.SyntaxKind.ExportDeclaration && (node as ts.ExportDeclaration).exportClause === undefined);
};
const normalizeFilePath = (path: string): string => {
return join(...path.split(/\//).map((part, index) => (part === '' && index === 0) ? sep : part));
};
export const getModulePathFromRoute = (parentPath: string, loadChildren: string, program: ts.Program, host: ts.CompilerHost) => {
const childModule = loadChildren.split('#')[0];
const { resolvedModule } = ts.resolveModuleName(childModule, parentPath, program.getCompilerOptions(), host);
if (resolvedModule) {
return normalizeFilePath(resolvedModule.resolvedFileName);
}
const childModuleFile = childModule + '.ts';
const parentSegments = dirname(parentPath).split(sep);
const childSegments = childModuleFile.split('/');
const max = Math.min(parentSegments.length, childSegments.length);
let maxCommon = 0;
for (let i = 1; i < max; i += 1) {
for (let j = 0; j < i; j += 1) {
let common = 0;
if (parentSegments[parentSegments.length - 1 - j] === childSegments[j]) {
common++;
maxCommon = Math.max(maxCommon, common);
} else {
// breaking here
common = 0;
j = i;
}
}
}
const path = join(
dirname(parentPath),
childModuleFile
.split('/')
.slice(maxCommon, childSegments.length)
.join('/')
);
// This early failure provides better error message compared to the
// generic "Multiple root routing modules" error.
if (!existsSync(path)) {
throw new Error(`The relative path "${loadChildren}" to "${parentPath}" cannot be resolved to a module`);
}
return path;
};
const imports = (
parent: string,
child: string,
program: ts.Program,
host: ts.CompilerHost,
importCache: {[parent: string]: {[child: string]: boolean}},
visited: { [key: string]: boolean } = {}
) => {
if (importCache[parent] && importCache[parent][child] !== undefined) {
return importCache[parent][child];
}
importCache[parent] = importCache[parent] || {};
const sf = program.getSourceFile(parent);
if (!sf) {
importCache[parent][child] = false;
return false;
}
if (visited[parent]) {
importCache[parent][child] = false;
return false;
}
visited[parent] = true;
let found = false;
sf.forEachChild(n => {
if (found) {
return;
}
if (!isImportDeclaration(n) && !isReExportDeclaration(n)) {
return;
}
const path = (n.moduleSpecifier as ts.StringLiteral).text;
const { resolvedModule } = ts.resolveModuleName(path, parent, program.getCompilerOptions(), host);
if (resolvedModule === undefined) {
return;
}
const fullPath = normalizeFilePath(resolvedModule.resolvedFileName);
if (fullPath === child) {
found = true;
}
// We don't want to dig into node_modules to find an entry point.
if (!found && existsSync(fullPath) && !fullPath.includes('node_modules')) {
found = imports(fullPath, child, program, host, importCache, visited);
}
});
importCache[parent][child] = found;
return found;
};
let cache: { [parent: string]: { [child: string]: boolean } } = {};
export const cleanModuleCache = () => (cache = {});
// This can potentially break if there's a lazy module
// that is not only loaded lazily but also imported
// inside of a parent module.
//
// For example, `app.module.ts` lazily loads `bar.module.ts`
// in the same time `app.module.ts` imports `bar.module.ts`
// this way the module entry point will be `app.module.ts`.
export const getModuleEntryPoint = (
path: string,
entryPoints: Set<string>,
program: ts.Program,
host: ts.CompilerHost
): string => {
const parents = [...entryPoints].filter(e => imports(e, path, program, host, cache));
// If no parents, this could be the root module
if (parents.length === 0) {
return path;
}
if (parents.length > 1) {
throw new Error(
`Module ${path} belongs to more than one module: ${parents.join(', ')}`
);
}
return parents[0];
};
export const getLazyEntryPoints = (
node: ts.ObjectLiteralExpression,
program: ts.Program,
host: ts.CompilerHost
) => {
const value = readLoadChildren(node, program.getTypeChecker());
if (!value) {
return null;
}
const parent = resolve(node.getSourceFile().fileName);
const module = getModulePathFromRoute(parent, value, program, host);
return module;
};
================================================
FILE: packages/guess-parser/src/angular/route-parser.ts
================================================
import * as ts from 'typescript';
import { RoutingModule } from '../../../common/interfaces';
import { existsSync, readFileSync } from 'fs';
import { dirname, resolve, join } from 'path';
import {
findMainModule,
getLazyEntryPoints,
getModuleEntryPoint,
Registry,
collectRoutingModules,
findRootModule,
cleanModuleCache
} from './modules';
import { LazyRoute, isRoute, getRoute, readChildren } from './routes';
export interface Options {
redirects: boolean;
}
const defaultOptions: Options = {
redirects: false,
};
const normalizeOptions = (options: Partial<Options>) => ({
...defaultOptions,
...options,
});
export const parseRoutes = (
tsconfig: string,
exclude: string[] = [],
inputOptions: Partial<Options> = {}
): RoutingModule[] => {
const options = normalizeOptions(inputOptions);
cleanModuleCache();
const parseConfigHost: ts.ParseConfigHost = {
fileExists: existsSync,
readDirectory: ts.sys.readDirectory,
readFile: file => readFileSync(file, 'utf8'),
useCaseSensitiveFileNames: true
};
const config = ts.readConfigFile(tsconfig, path =>
readFileSync(path).toString()
);
const parsed = ts.parseJsonConfigFileContent(
config.config,
parseConfigHost,
resolve(dirname(tsconfig)),
{
noEmit: true
}
);
const host = ts.createCompilerHost(parsed.options, true);
const program = ts.createProgram(parsed.fileNames, parsed.options, host);
const typeChecker = program.getTypeChecker();
const toAbsolute = (file: string) =>
file.startsWith('/') || file.startsWith(process.cwd()) ? file : join(process.cwd(), file);
const excludeFiles = new Set<string>(exclude.map(toAbsolute));
const visitTopLevelRoutes = (
s: ts.SourceFile,
callback: (routeObj: ts.Node) => void,
n: ts.Node
) => {
if (excludeFiles.has(resolve(s.fileName))) {
return;
}
if (!n) {
return;
}
if (isRoute(n, typeChecker, options.redirects)) {
callback(n);
} else {
n.forEachChild(visitTopLevelRoutes.bind(null, s, callback));
}
};
const mainPath = findMainModule(program);
if (!mainPath) {
throw new Error('Cannot find the main application module');
}
const entryPoints: Set<string> = new Set([mainPath]);
const collectEntryPoints = (n: ts.Node) => {
const path = getLazyEntryPoints(
n as ts.ObjectLiteralExpression,
program,
host
);
if (!path) {
const childrenArray = readChildren(n as ts.ObjectLiteralExpression);
if (childrenArray) {
childrenArray.forEach(collectEntryPoints);
}
return;
}
entryPoints.add(path);
};
program.getSourceFiles().map(s => {
s.forEachChild(
visitTopLevelRoutes.bind(null, s, collectEntryPoints)
);
});
const registry: Registry = {};
program.getSourceFiles().map(s => {
s.forEachChild(
visitTopLevelRoutes.bind(null, s, (n: ts.Node) => {
const path = resolve(n.getSourceFile().fileName);
const route = getRoute(
n as ts.ObjectLiteralExpression,
entryPoints,
program,
host
);
if (!route) {
return;
}
const modulePath = getModuleEntryPoint(path, entryPoints, program, host);
const current = registry[modulePath] || {
lazyRoutes: [],
eagerRoutes: []
};
if ((route as LazyRoute).module) {
current.lazyRoutes.push(route as LazyRoute);
} else {
current.eagerRoutes.push(route);
}
registry[modulePath] = current;
})
);
});
const result: RoutingModule[] = [];
if (Object.keys(registry).length > 0) {
collectRoutingModules(findRootModule(registry), registry, result);
}
return result;
};
================================================
FILE: packages/guess-parser/src/angular/routes.ts
================================================
import * as ts from 'typescript';
import { evaluate } from 'ts-evaluator';
import { getModuleEntryPoint, getModulePathFromRoute } from './modules';
import { resolve } from 'path';
const getObjectProp = (
node: ts.ObjectLiteralExpression,
prop: string
): ts.Expression | null => {
const vals = node.properties.values();
for (const val of vals) {
if (val.kind !== ts.SyntaxKind.PropertyAssignment) {
continue;
}
const value = val as ts.PropertyAssignment;
if (value.name.kind !== ts.SyntaxKind.Identifier) {
continue;
}
const name = value.name.text;
if (name === prop) {
return value.initializer;
}
}
return null;
};
export const readLoadChildren = (
node: ts.ObjectLiteralExpression,
typeChecker: ts.TypeChecker
): string | null => {
const expr = getObjectProp(node, 'loadChildren');
if (!expr) {
return null;
}
if (expr.kind === ts.SyntaxKind.StringLiteral) {
return (expr as ts.StringLiteral).text;
}
let result: string | null = null;
const visitor = (n: ts.Node) => {
if (n.kind === ts.SyntaxKind.ImportKeyword) {
const parent = n.parent as ts.CallExpression;
const arg = parent.arguments[0];
const res = evaluate({
node: arg,
typeChecker: typeChecker
});
if (res.success) {
result = res.value as string;
}
}
if (result) {
return;
}
n.forEachChild(visitor);
};
expr.forEachChild(visitor);
// Fallback to when loadChildren looks like:
// loadChildren: 'foo' + '/' + 'bar'
if (!result) {
const res = evaluate({
node: expr,
typeChecker: typeChecker
});
if (res.success) {
result = res.value as string;
}
}
return result;
};
const readPath = (
node: ts.ObjectLiteralExpression,
typeChecker: ts.TypeChecker
): string | null => {
const expr = getObjectProp(node, 'path');
if (!expr) {
return null;
}
const val = evaluate({
node: expr,
typeChecker
});
if (val.success) {
return val.value as string;
}
return null;
};
const readRedirect = (
node: ts.ObjectLiteralExpression,
typeChecker: ts.TypeChecker
): string | null => {
const expr = getObjectProp(node, 'redirectTo');
if (!expr) {
return null;
}
const val = evaluate({
node: expr,
typeChecker
});
if (val.success) {
return val.value as string;
}
return null;
};
export const readChildren = (
node: ts.ObjectLiteralExpression,
): ts.NodeArray<ts.Node> | null => {
const expr = getObjectProp(node, 'children');
if (!expr) {
return null;
}
return (expr as ts.ArrayLiteralExpression).elements;
};
export interface Route {
path: string;
children: Route[];
redirectTo?: string;
}
export interface LazyRoute extends Route {
module: string;
}
export const getRoute = (
node: ts.ObjectLiteralExpression,
entryPoints: Set<string>,
program: ts.Program,
host: ts.CompilerHost
): Route | null => {
const path = readPath(node, program.getTypeChecker());
if (path === null) {
return null;
}
const childrenArray = readChildren(node);
let children: Route[] = [];
if (childrenArray) {
children = childrenArray
.map(c => {
if (c.kind !== ts.SyntaxKind.ObjectLiteralExpression) {
return null;
}
return getRoute(c as ts.ObjectLiteralExpression, entryPoints, program, host);
})
.filter(e => e !== null) as Route[];
}
const route: Route = { path, children };
const redirectTo = readRedirect(node, program.getTypeChecker());
if (redirectTo) {
route.redirectTo = redirectTo;
}
const loadChildren = readLoadChildren(node, program.getTypeChecker());
if (loadChildren) {
const parent = getModuleEntryPoint(
resolve(node.getSourceFile().fileName),
entryPoints,
program,
host
);
const module = getModulePathFromRoute(parent, loadChildren, program, host);
return {
...route,
module
} as LazyRoute;
}
return route;
};
export const isRoute = (n: ts.Node, typeChecker: ts.TypeChecker, redirects: boolean): boolean => {
if (
n.kind !== ts.SyntaxKind.ObjectLiteralExpression ||
!n.parent ||
n.parent.kind !== ts.SyntaxKind.ArrayLiteralExpression
) {
return false;
}
const objLiteral = n as ts.ObjectLiteralExpression;
const path = readPath(objLiteral, typeChecker) !== null;
const redirectTo = redirects && readRedirect(objLiteral, typeChecker) !== null;
const children = !!readChildren(objLiteral);
const loadChildren = !!readLoadChildren(objLiteral, typeChecker);
const component = !!getObjectProp(objLiteral, 'component');
return (path && children) || (path && component) || (path && loadChildren) || (path && redirectTo);
};
================================================
FILE: packages/guess-parser/src/detector/detect.ts
================================================
import { readFileSync, existsSync } from 'fs';
import { join } from 'path';
import { ProjectType, ProjectMetadata } from '../../../common/interfaces';
const dep = (p: any) => (name: string) => (p.dependencies ? p.dependencies[name] : undefined);
const devDep = (p: any) => (name: string) => (p.devDependencies ? p.devDependencies[name] : undefined);
export const detect = (base: string): ProjectMetadata | undefined => {
const path = ['package.json', '../package.json']
.map(p => join(base, p))
.filter(existsSync)
.pop();
if (!path) {
throw new Error('Unable to discover the project type');
}
const content = JSON.parse(readFileSync(path).toString()) as any;
const exists = (file: string) => existsSync(join(base, file));
const d = dep(content);
const dd = devDep(content);
const tsconfig = 'tsconfig.app.json';
const srcTsConfig = join('src', tsconfig);
if (dd('@angular/cli') && (exists(srcTsConfig) || exists(tsconfig))) {
let tsconfigPath = tsconfig;
if (exists(srcTsConfig)) {
tsconfigPath = srcTsConfig;
}
return {
type: ProjectType.AngularCLI,
version: dd('@angular/cli'),
details: {
typescript: dd('typescript'),
tsconfigPath,
sourceDir: 'src'
}
};
}
if (d('gatsby')) {
return {
type: ProjectType.Gatsby,
version: d('gatsby')
};
}
if (d('react') && d('react-scripts-ts') && exists('tsconfig.json')) {
return {
type: ProjectType.CreateReactAppTypeScript,
version: d('react-scripts-ts'),
details: {
typescript: dd('typescript'),
tsconfigPath: join(base, 'tsconfig.json'),
sourceDir: 'src'
}
};
}
if (d('react') && d('react-scripts')) {
return {
type: ProjectType.CreateReactApp,
version: d('react-scripts'),
details: {
sourceDir: 'src'
}
};
}
if (d('preact') && dd('preact-cli')) {
return {
type: ProjectType.PreactCLI,
version: dd('preact-cli'),
details: {
sourceDir: '.'
}
};
}
return undefined;
};
================================================
FILE: packages/guess-parser/src/detector/index.ts
================================================
export * from './detect';
================================================
FILE: packages/guess-parser/src/language-service.ts
================================================
import { existsSync, readFileSync } from 'fs';
import * as ts from 'typescript';
export const getLanguageService = (rootFileNames: string[], options: ts.CompilerOptions) => {
const files: ts.MapLike<{ version: number }> = {};
// initialize the list of files
rootFileNames.forEach(fileName => {
files[fileName] = { version: 0 };
});
const servicesHost: ts.LanguageServiceHost = {
getScriptFileNames: () => rootFileNames,
getScriptVersion: fileName => files[fileName] && files[fileName].version.toString(),
getScriptSnapshot: fileName => {
if (!existsSync(fileName)) {
return undefined;
}
return ts.ScriptSnapshot.fromString(readFileSync(fileName).toString());
},
getCurrentDirectory: () => process.cwd(),
getCompilationSettings: () => options,
getDefaultLibFileName: o => ts.getDefaultLibFilePath(o)
};
return ts.createLanguageService(servicesHost, ts.createDocumentRegistry());
};
================================================
FILE: packages/guess-parser/src/parser.ts
================================================
import { parseRoutes as ngParseRoutes } from './angular';
import { parseReactTSXRoutes, parseReactJSXRoutes } from './react';
import { parsePreactJSXRoutes } from './preact';
import { RoutingModule, ProjectType } from '../../common/interfaces';
import { detect } from './detector';
import { join } from 'path';
const unique = (a: RoutingModule[]) => {
const map: { [path: string]: RoutingModule } = {};
a.forEach(r => (map[r.path] = r));
return Object.keys(map).map(k => map[k]);
};
export const parseRoutes = (base: string) => {
let result: RoutingModule[] | undefined = undefined;
const app = detect(base);
if (!app) {
throw new Error('Cannot detect the application type');
}
if (app.type === ProjectType.AngularCLI && app.details && app.details.tsconfigPath) {
result = ngParseRoutes(join(base, app.details.tsconfigPath));
}
if (app.type === ProjectType.CreateReactAppTypeScript && app.details && app.details.tsconfigPath) {
result = parseReactTSXRoutes(app.details.tsconfigPath);
}
if (app.type === ProjectType.CreateReactApp && app.details && app.details.sourceDir) {
result = parseReactJSXRoutes(join(base, app.details.sourceDir));
}
if (app.type === ProjectType.PreactCLI && app.details && app.details.sourceDir) {
result = parsePreactJSXRoutes(join(base, app.details.sourceDir));
}
if (!result) {
throw new Error('Unknown project type');
}
const res = unique(result);
res.filter(r => !r.parentModulePath || r.path === '/').forEach(r => (r.parentModulePath = null));
return res;
};
================================================
FILE: packages/guess-parser/src/preact/index.ts
================================================
import * as ts from 'typescript';
import * as path from 'path';
import { RoutingModule } from '../../../common/interfaces';
import { readFiles } from '../utils';
import { getLanguageService } from '../language-service';
const LazyRe = /routes\/((\w+\/index)|\w+)\.(js|jsx|ts|tsx)$/;
const getLazyDefinition = (
filename: string,
identifier: ts.Node,
ls: ts.LanguageService
): ts.DefinitionInfo | undefined => {
const defs = ls.getDefinitionAtPosition(filename, identifier.pos + 1);
if (!defs) {
return undefined;
}
return defs.filter(d => LazyRe.test(d.fileName) && d.kind === 'class').pop();
};
const extractModule = (a: ts.JsxAttribute) => {
const init = a.initializer as ts.JsxExpression | null;
if (!init) {
return null;
}
const arrow = init.expression as ts.ArrowFunction | null;
if (!arrow) {
return '';
}
const body = arrow.body as ts.CallExpression | null;
if (!body) {
return '';
}
const temp = body.expression as ts.CallExpression | null;
if (!temp) {
return '';
}
const internalExpr = temp.expression as ts.CallExpression | null;
if (!internalExpr) {
return '';
}
const arg = internalExpr.arguments[0] as ts.StringLiteral | null;
if (!arg || arg.kind !== ts.SyntaxKind.StringLiteral) {
return '';
}
return (arg as ts.StringLiteral).text;
};
const extractRoute = (c: ts.Node, file: ts.SourceFile, ls: ts.LanguageService) => {
if (c.kind !== ts.SyntaxKind.JsxElement && c.kind !== ts.SyntaxKind.JsxSelfClosingElement) {
return;
}
let el: ts.JsxSelfClosingElement | ts.JsxOpeningElement = (c as ts.JsxElement).openingElement;
if (c.kind === ts.SyntaxKind.JsxSelfClosingElement) {
el = c as ts.JsxSelfClosingElement;
}
const module: Partial<RoutingModule> = {
lazy: (el.tagName as ts.Identifier).text === 'AsyncRoute',
parentModulePath: file.fileName,
modulePath: file.fileName
};
const def = getLazyDefinition(file.getSourceFile().fileName, el, ls);
if (def) {
module.lazy = true;
module.modulePath = def.fileName;
}
el.attributes.properties.forEach(p => {
const { text } = p.name as ts.Identifier;
if (text === 'path') {
module.path = ((p as ts.JsxAttribute).initializer as ts.StringLiteral).text;
}
if (text === 'getComponent') {
const parts = file.fileName.split('/');
parts.pop();
const tempName = extractModule(p as ts.JsxAttribute);
if (tempName) {
const name = tempName + '.tsx';
module.modulePath = '/' + path.join(...parts.concat([name]));
module.lazy = true;
}
}
});
return module as RoutingModule;
};
const extractRoutes = (file: ts.SourceFile, ls: ts.LanguageService): RoutingModule[] => {
const result: RoutingModule[] = [];
const stack: ts.Node[] = [file];
while (stack.length) {
const c = stack.pop();
if (!c) {
return result;
}
const el: ts.JsxSelfClosingElement | ts.JsxOpeningElement = (c as ts.JsxElement).openingElement;
if (c.kind === ts.SyntaxKind.JsxElement && (el.tagName as ts.Identifier).text === 'Router') {
(c as ts.JsxElement).children.forEach((e: ts.Node) => {
const route = extractRoute(e, file, ls);
if (route) {
result.push(route);
}
});
} else {
c.getChildren(file).forEach(child => stack.push(child));
}
}
return result;
};
export const parsePreactJSXRoutes = (base: string): RoutingModule[] => {
const options = {
jsx: ts.JsxEmit.React,
allowJs: true
};
const rootFileNames = readFiles(base);
const program = ts.createProgram(rootFileNames, options);
const jsxFiles = program
.getSourceFiles()
.filter(f => f.fileName.endsWith('.tsx') || f.fileName.endsWith('.jsx') || f.fileName.endsWith('.js'));
const routes = jsxFiles.reduce(
(a, f) => a.concat(extractRoutes(f, getLanguageService(rootFileNames, options))),
[] as RoutingModule[]
);
const routeMap = routes.reduce(
(a, m) => {
a[m.path] = m;
return a;
},
{} as { [key: string]: RoutingModule }
);
return Object.keys(routeMap).map(k => routeMap[k]);
};
================================================
FILE: packages/guess-parser/src/react/base.ts
================================================
import * as ts from 'typescript';
import * as path from 'path';
import { RoutingModule } from '../../../common/interfaces';
const extractRoutes = (file: ts.SourceFile): RoutingModule[] => {
const result: RoutingModule[] = [];
const stack: ts.Node[] = [file];
const extractModule = (a: ts.JsxAttribute) => {
const init = a.initializer as ts.JsxExpression | null;
if (!init) {
return null;
}
const expr = init.expression as ts.CallExpression | null;
if (!expr) {
return '';
}
if (!expr.arguments) {
return '';
}
const arrow = expr.arguments[0] as ts.ArrowFunction | null;
if (!arrow) {
return '';
}
const body = arrow.body as ts.CallExpression;
if (!body) {
return '';
}
const arg = body.arguments[0];
if (!arg || arg.kind !== ts.SyntaxKind.StringLiteral) {
return '';
}
return (arg as ts.StringLiteral).text;
};
while (stack.length) {
const c = stack.pop();
if (!c) {
return result;
}
if (c.kind === ts.SyntaxKind.JsxElement || c.kind === ts.SyntaxKind.JsxSelfClosingElement) {
let el: ts.JsxSelfClosingElement | ts.JsxOpeningElement = (c as ts.JsxElement).openingElement;
if (c.kind === ts.SyntaxKind.JsxSelfClosingElement) {
el = c as ts.JsxSelfClosingElement;
}
if ((el.tagName as ts.Identifier).text === 'Route') {
const module: Partial<RoutingModule> = {
lazy: false,
parentModulePath: file.fileName,
modulePath: file.fileName
};
el.attributes.properties.forEach(p => {
const { text } = p.name as ts.Identifier;
if (text === 'path') {
module.path = ((p as ts.JsxAttribute).initializer as ts.StringLiteral).text;
}
if (text === 'component') {
const parts = file.fileName.split('/');
parts.pop();
const tempName = extractModule(p as ts.JsxAttribute);
if (tempName) {
const name = tempName + '.tsx';
module.modulePath = '/' + path.join(...parts.concat([name]));
module.lazy = true;
}
}
result.push(module as RoutingModule);
});
}
}
c.getChildren(file).forEach(child => {
stack.push(child);
});
}
return result;
};
export const parseReactRoutes = (files: string[], options: ts.CompilerOptions) => {
const program = ts.createProgram(files, options);
const jsxFiles = program.getSourceFiles().filter(f => f.fileName.endsWith('.tsx') || f.fileName.endsWith('.jsx'));
const routes = jsxFiles.reduce((a, f) => a.concat(extractRoutes(f)), [] as RoutingModule[]);
const modules = routes.reduce(
(a, r) => {
a[r.modulePath] = true;
return a;
},
{} as { [key: string]: boolean }
);
const rootModule = routes.filter(r => r.parentModulePath && !modules[r.parentModulePath]).pop();
if (rootModule) {
routes.push({
path: '/',
parentModulePath: null,
modulePath: rootModule.parentModulePath,
lazy: false
} as RoutingModule);
}
const routeMap = routes.reduce(
(a, m) => {
a[m.path] = m;
return a;
},
{} as { [key: string]: RoutingModule }
);
return Object.keys(routeMap).map(k => routeMap[k]);
};
================================================
FILE: packages/guess-parser/src/react/index.ts
================================================
export { parseReactRoutes } from './base';
export { parseRoutes as parseReactTSXRoutes } from './react-tsx';
export { parseRoutes as parseReactJSXRoutes } from './react-jsx';
================================================
FILE: packages/guess-parser/src/react/react-jsx.ts
================================================
import { parseReactRoutes } from '.';
import { JsxEmit } from 'typescript';
import { RoutingModule } from '../../../common/interfaces';
import { readFiles } from '../utils';
export const parseRoutes = (base: string): RoutingModule[] => {
return parseReactRoutes(readFiles(base), {
jsx: JsxEmit.React,
allowJs: true
});
};
================================================
FILE: packages/guess-parser/src/react/react-tsx.ts
================================================
import { readFileSync, lstatSync, existsSync } from 'fs';
import * as ts from 'typescript';
import { join, dirname, resolve } from 'path';
import { parseReactRoutes } from './';
import { RoutingModule } from '../../../common/interfaces';
const parseConfigHost = {
useCaseSensitiveFileNames: true,
fileExists: existsSync,
readDirectory: ts.sys.readDirectory,
readFile: ts.sys.readFile
};
const calcProjectFileAndBasePath = (project: string): { projectFile: string; basePath: string } => {
const projectIsDir = lstatSync(project).isDirectory();
const projectFile = projectIsDir ? join(project, 'tsconfig.json') : project;
const projectDir = projectIsDir ? project : dirname(project);
const basePath = resolve(process.cwd(), projectDir);
return { projectFile, basePath };
};
export const parseRoutes = (tsconfig: string): RoutingModule[] => {
const { config, error } = ts.readConfigFile(tsconfig, (f: string) => readFileSync(f).toString());
if (error) {
throw error;
}
const { basePath } = calcProjectFileAndBasePath(tsconfig);
const parsed = ts.parseJsonConfigFileContent(config, parseConfigHost, basePath);
return parseReactRoutes(parsed.fileNames, parsed.options);
};
================================================
FILE: packages/guess-parser/src/utils.ts
================================================
import { statSync, readdirSync } from 'fs';
import { join } from 'path';
export const readFiles = (dir: string): string[] => {
if (dir === 'node_modules') {
return [];
}
const result = readdirSync(dir).map(node => join(dir, node));
const files = result.filter(
node => statSync(node).isFile() && (node.endsWith('.jsx') || node.endsWith('.js'))
);
const dirs = result.filter(node => statSync(node).isDirectory());
return [].concat.apply(files, (dirs.map(readFiles) as unknown) as ConcatArray<never>[]);
};
================================================
FILE: packages/guess-parser/test/angular.spec.ts
================================================
import { parseRoutes } from '../src/angular';
const fixtureRoutes = new Set([
'/foo',
'/foo/baz',
'/foo/index',
'/foo/baz/index',
'/bar/baz',
'/qux',
'/library',
'/bar-simple',
'/foo/child1',
'/foo/foo-parent',
'/foo/foo-parent/child2',
'/eager',
'/eager/lazy',
]);
const fixtureRoutesWithRedirects = new Set([
...fixtureRoutes,
''
]);
const nxRoutes = new Set([
'/login',
'/home',
'/customers/list',
'/customers'
]);
const nxRoutesWithRedirects = new Set([
...nxRoutes,
''
]);
describe('Angular parser', () => {
it('should parse an app', () => {
expect(() =>
parseRoutes(
'packages/guess-parser/test/fixtures/angular/src/tsconfig.app.json'
)
).not.toThrow();
});
it('should produce routes', () => {
const routes = parseRoutes(
'packages/guess-parser/test/fixtures/angular/src/tsconfig.app.json'
);
expect(routes instanceof Array).toBeTruthy();
const allRoutes = new Set(routes.map(r => r.path));
[...allRoutes].forEach(r => expect(fixtureRoutes).toContain(r));
expect(allRoutes.size).toEqual(fixtureRoutes.size);
});
it('should consider redirects', () => {
const routes = parseRoutes(
'packages/guess-parser/test/fixtures/angular/src/tsconfig.app.json'
, undefined, { redirects: true });
expect(routes instanceof Array).toBeTruthy();
const allRoutes = new Set(routes.map(r => r.path));
[...allRoutes].forEach(r => expect(fixtureRoutesWithRedirects).toContain(r));
expect(allRoutes.size).toEqual(fixtureRoutesWithRedirects.size);
const redirect = routes.filter(route => route.path === '').pop();
expect(redirect?.redirectTo).toEqual('bar');
expect(redirect?.lazy).toEqual(false);
});
it('should produce routes with proper paths', () => {
const routes = parseRoutes(
'packages/guess-parser/test/fixtures/angular/src/tsconfig.app.json'
);
const route = routes.find(r => r.path === '/foo');
expect(route!.modulePath.endsWith('foo.module.ts')).toBeTruthy();
expect(route!.lazy).toBeTruthy();
expect(route!.parentModulePath!.endsWith('app.module.ts')).toBeTruthy();
});
it('should work with nx monorepo with path mappings', () => {
const routes = parseRoutes(
'packages/guess-parser/test/fixtures/nx/apps/ng-cli-app/tsconfig.app.json'
).map(r => r.path);
[...routes].forEach(r => expect(nxRoutes).toContain(r));
});
it('should discover redirects', () => {
const routes = parseRoutes(
'packages/guess-parser/test/fixtures/nx/apps/ng-cli-app/tsconfig.app.json'
, undefined, { redirects: true }).map(r => r.path);
[...routes].forEach(r => expect(nxRoutesWithRedirects).toContain(r));
});
});
================================================
FILE: packages/guess-parser/test/detect.spec.ts
================================================
import { detect } from '../src/detector';
import { ProjectType } from '../../common/interfaces';
describe('detect', () => {
describe('angular', () => {
it('should detect an Angular app', () => {
expect(detect('packages/guess-parser/test/fixtures/angular')!.type).toBe(ProjectType.AngularCLI);
});
it('should detect an Angular version 8 app', () => {
expect(detect('packages/guess-parser/test/fixtures/ng8')!.type).toBe(ProjectType.AngularCLI);
});
});
describe('create-react-app', () => {
it('should detect an create-react-app', () => {
expect(detect('packages/guess-parser/test/fixtures/react-app')!.type).toBe(ProjectType.CreateReactApp);
});
});
describe('create-react-app-ts', () => {
it('should detect an create-react-app-ts', () => {
expect(detect('packages/guess-parser/test/fixtures/react-app-ts')!.type).toBe(
ProjectType.CreateReactAppTypeScript
);
});
});
describe('gatsby', () => {
it('should detect an gatsby', () => {
expect(detect('packages/guess-parser/test/fixtures/gatsby')!.type).toBe(ProjectType.Gatsby);
});
});
describe('unknown', () => {
it('should not detect unknown app', () => {
expect(detect('packages/guess-parser/test/fixtures/unknown')).toBe(undefined);
});
});
});
================================================
FILE: packages/guess-parser/test/fixtures/angular/.angular-cli.json
================================================
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"project": {
"name": "angular"
},
"apps": [
{
"root": "src",
"outDir": "dist",
"assets": [
"assets",
"favicon.ico"
],
"index": "index.html",
"main": "main.ts",
"polyfills": "polyfills.ts",
"test": "test.ts",
"tsconfig": "tsconfig.app.json",
"testTsconfig": "tsconfig.spec.json",
"prefix": "app",
"styles": [
"styles.css"
],
"scripts": [],
"environmentSource": "environments/environment.ts",
"environments": {
"dev": "environments/environment.ts",
"prod": "environments/environment.prod.ts"
}
}
],
"e2e": {
"protractor": {
"config": "./protractor.conf.js"
}
},
"lint": [
{
"project": "src/tsconfig.app.json",
"exclude": "**/node_modules/**"
},
{
"project": "src/tsconfig.spec.json",
"exclude": "**/node_modules/**"
},
{
"project": "e2e/tsconfig.e2e.json",
"exclude": "**/node_modules/**"
}
],
"test": {
"karma": {
"config": "./karma.conf.js"
}
},
"defaults": {
"styleExt": "css",
"component": {}
}
}
================================================
FILE: packages/guess-parser/test/fixtures/angular/library/index.ts
================================================
export * from './nested/library.module';
================================================
FILE: packages/guess-parser/test/fixtures/angular/library/library.module.ts
================================================
import { NgModule, Component } from '@angular/core';
import { RouterModule } from '@angular/router';
@Component({
})
class LibraryComponent {}
@NgModule({
declarations: [LibraryComponent],
imports: [RouterModule.forChild([
{
path: '',
component: LibraryComponent,
pathMatch: 'full'
}
])],
bootstrap: [LibraryComponent]
})
export class LibraryModule {}
================================================
FILE: packages/guess-parser/test/fixtures/angular/library/nested/library.module.ts
================================================
import { NgModule, Component } from '@angular/core';
import { RouterModule } from '@angular/router';
@Component({
})
class LibraryComponent {}
@NgModule({
declarations: [LibraryComponent],
imports: [RouterModule.forChild([
{
path: '',
component: LibraryComponent,
pathMatch: 'full'
}
])],
bootstrap: [LibraryComponent]
})
export class LibraryModule {}
================================================
FILE: packages/guess-parser/test/fixtures/angular/library/tsconfig.json
================================================
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/library",
"module": "es2015",
"types": []
},
"include": ["**/*.ts"]
}
================================================
FILE: packages/guess-parser/test/fixtures/angular/package.json
================================================
{
"name": "angular",
"version": "0.0.0",
"license": "MIT",
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build --prod",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
},
"private": true,
"dependencies": {
"@angular/animations": "^5.2.0",
"@angular/common": "^5.2.0",
"@angular/compiler": "^5.2.0",
"@angular/core": "^5.2.0",
"@angular/forms": "^5.2.0",
"@angular/http": "^5.2.0",
"@angular/platform-browser": "^5.2.0",
"@angular/platform-browser-dynamic": "^5.2.0",
"@angular/router": "^5.2.0",
"core-js": "^2.4.1",
"rxjs": "^5.5.6",
"zone.js": "^0.8.19"
},
"devDependencies": {
"@angular/cli": "~1.7.1",
"@angular/compiler-cli": "^5.2.0",
"@angular/language-service": "^5.2.0",
"@types/jasmine": "~2.8.3",
"@types/jasminewd2": "~2.0.2",
"@types/node": "~6.0.60",
"codelyzer": "^4.0.1",
"jasmine-core": "~2.8.0",
"jasmine-spec-reporter": "~4.2.1",
"karma": "~2.0.0",
"karma-chrome-launcher": "~2.2.0",
"karma-coverage-istanbul-reporter": "^1.2.1",
"karma-jasmine": "~1.1.0",
"karma-jasmine-html-reporter": "^0.2.2",
"protractor": "~5.1.2",
"ts-node": "~4.1.0",
"tslint": "~5.9.1",
"typescript": "~2.5.3"
}
}
================================================
FILE: packages/guess-parser/test/fixtures/angular/src/app/about/about-routing.module.ts
================================================
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
const routes: Routes = [];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class AboutRoutingModule {}
================================================
FILE: packages/guess-parser/test/fixtures/angular/src/app/about/about.module.ts
================================================
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { AboutRoutingModule } from './about-routing.module';
@NgModule({
declarations: [],
imports: [
CommonModule,
AboutRoutingModule
]
})
export class AboutModule { }
================================================
FILE: packages/guess-parser/test/fixtures/angular/src/app/app-routing.module.ts
================================================
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { BarSimpleComponent } from './bar-simple.component';
const module = './foo/foo.module';
const routes: Routes = [
// {
// path: 'foo',
// component: BarSimpleComponent,
// children: [
// {
// path: 'qux',
// component: BarSimpleComponent,
// children: [
// {
// path: 'bar',
// loadChildren: () => import('./bar/bar.module').then(m => m.BarModule)
// }
// ]
// }
// ]
// },
{
path: 'fo' + 'o',
loadChildren: () => import(module).then(e => e.FooModule)
},
{
path: 'bar',
loadChildren: () => import('./bar/bar.module').then(m => m.BarModule)
},
{
path: 'qux',
loadChildren: 'app/qux/qux.module#QuxModule'
},
{
path: 'library',
loadChildren: 'app/wrapper/wrapper.module#WrapperModule'
},
{
path: 'bar-simple',
component: BarSimpleComponent
},
{
path: 'about',
loadChildren: () => import('./about/about.module').then(m => m.AboutModule)
},
{
path: 'eager',
children: [
{
path: 'lazy',
loadChildren: () => import('./lazy/lazy.module').then(m => m.LazyModule)
}
]
},
{
path: '',
pathMatch: 'full',
redirectTo: 'bar'
}
];
@NgModule({
declarations: [BarSimpleComponent],
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule {}
================================================
FILE: packages/guess-parser/test/fixtures/angular/src/app/app.component.css
================================================
================================================
FILE: packages/guess-parser/test/fixtures/angular/src/app/app.component.html
================================================
<a routerLink="/foo">Foo</a>
<a routerLink="/bar-simple">Bar</a>
<a routerLink="/bar/baz">Baz</a>
<router-outlet></router-outlet>
================================================
FILE: packages/guess-parser/test/fixtures/angular/src/app/app.component.spec.ts
================================================
import { TestBed, async } from '@angular/core/testing';
import { AppComponent } from './app.component';
describe('AppComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [
AppComponent
],
}).compileComponents();
}));
it('should create the app', async(() => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance;
expect(app).toBeTruthy();
}));
it(`should have as title 'app'`, async(() => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance;
expect(app.title).toEqual('app');
}));
it('should render title in a h1 tag', async(() => {
const fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges();
const compiled = fixture.debugElement.nativeElement;
expect(compiled.querySelector('h1').textContent).toContain('Welcome to app!');
}));
});
================================================
FILE: packages/guess-parser/test/fixtures/angular/src/app/app.component.ts
================================================
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'app';
}
================================================
FILE: packages/guess-parser/test/fixtures/angular/src/app/app.module.ts
================================================
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { AppRoutingModule } from './app-routing.module';
@NgModule({
declarations: [AppComponent],
imports: [BrowserModule, AppRoutingModule],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule {}
================================================
FILE: packages/guess-parser/test/fixtures/angular/src/app/bar/bar.module.ts
================================================
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
@NgModule({
imports: [
RouterModule.forChild([
{
path: 'baz',
loadChildren: () => import('./baz/baz.module').then(m => m.BazModule)
}
])
]
})
export class BarModule {}
================================================
FILE: packages/guess-parser/test/fixtures/angular/src/app/bar/baz/baz.module.ts
================================================
import { NgModule, Component } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import './cycle-parent';
@Component({
selector: 'app-baz',
template: 'Baz'
})
export class BazComponent {}
const routes: Routes = [
{
path: '',
pathMatch: 'full',
component: BazComponent
}
];
@NgModule({
declarations: [BazComponent],
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class BazModule {}
================================================
FILE: packages/guess-parser/test/fixtures/angular/src/app/bar/baz/cycle-parent.ts
================================================
import './baz.module';
================================================
FILE: packages/guess-parser/test/fixtures/angular/src/app/bar-simple.component.ts
================================================
import { Component } from '@angular/core';
@Component({
selector: 'app-component',
template: 'Bar Simple'
})
export class BarSimpleComponent {}
================================================
FILE: packages/guess-parser/test/fixtures/angular/src/app/foo/baz/baz-routing.module.ts
================================================
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { BazComponent } from './baz.component';
const routes: Routes = [
{
path: '',
component: BazComponent
},
{
path: 'index',
component: BazComponent
}
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class BazRoutingModule {}
================================================
FILE: packages/guess-parser/test/fixtures/angular/src/app/foo/baz/baz.component.ts
================================================
import { Component } from '@angular/core';
@Component({
selector: 'app-baz',
template: 'baz-component'
})
export class BazComponent {}
================================================
FILE: packages/guess-parser/test/fixtures/angular/src/app/foo/baz/baz.module.ts
================================================
import { NgModule } from '@angular/core';
import { BazComponent } from './baz.component';
import { BazRoutingModule } from './baz-routing.module';
@NgModule({
declarations: [BazComponent],
imports: [BazRoutingModule],
bootstrap: [BazComponent]
})
export class FooModule {}
================================================
FILE: packages/guess-parser/test/fixtures/angular/src/app/foo/foo-routing.module.ts
================================================
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { FooComponent } from './foo.component';
const baz = 'baz';
const routes: Routes = [
{
path: '',
pathMatch: 'full',
component: FooComponent
},
{
path: 'index',
component: FooComponent
},
{
path: 'baz',
loadChildren: baz + '/baz.module#BazModule'
},
{
path: '',
children: [
{
path: 'child1',
component: FooComponent
}
]
},
{
path: 'foo-parent',
children: [
{
path: 'child2',
component: FooComponent
}
]
}
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class FooRoutingModule {}
================================================
FILE: packages/guess-parser/test/fixtures/angular/src/app/foo/foo.component.ts
================================================
import { Component } from '@angular/core';
@Component({
selector: 'app-foo',
template: 'foo-component'
})
export class FooComponent {}
================================================
FILE: packages/guess-parser/test/fixtures/angular/src/app/foo/foo.module.ts
================================================
import { NgModule, Component } from '@angular/core';
import { FooComponent } from './foo.component';
import { FooRoutingModule } from './foo-routing.module';
@NgModule({
declarations: [FooComponent],
imports: [FooRoutingModule],
bootstrap: [FooComponent]
})
export class FooModule {}
================================================
FILE: packages/guess-parser/test/fixtures/angular/src/app/lazy/lazy-routing.module.ts
================================================
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { LazyComponent } from './lazy.component';
const routes: Routes = [{ path: '', component: LazyComponent }];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class LazyRoutingModule {}
================================================
FILE: packages/guess-parser/test/fixtures/angular/src/app/lazy/lazy.component.ts
================================================
import { Component } from '@angular/core';
@Component({
template: '',
})
export class LazyComponent { }
================================================
FILE: packages/guess-parser/test/fixtures/angular/src/app/lazy/lazy.module.ts
================================================
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { LazyRoutingModule } from './lazy-routing.module';
import { LazyComponent } from './lazy.component';
@NgModule({
declarations: [
LazyComponent
],
imports: [
CommonModule,
LazyRoutingModule
]
})
export class LazyModule { }
================================================
FILE: packages/guess-parser/test/fixtures/angular/src/app/qux/qux.module.ts
================================================
import { NgModule, Component } from '@angular/core';
import { RouterModule } from '@angular/router';
@Component({
})
class QuxComponent {}
@NgModule({
declarations: [QuxComponent],
imports: [RouterModule.forChild([
{
path: '',
component: QuxComponent,
pathMatch: 'full'
}
])],
bootstrap: [QuxComponent]
})
export class QuxModule {}
================================================
FILE: packages/guess-parser/test/fixtures/angular/src/app/wrapper/wrapper.module.ts
================================================
import { NgModule } from '@angular/core';
import { LibraryModule } from '~library';
@NgModule({
imports: [LibraryModule]
})
export class WrapperModule {}
import { Component } from '@angular/core';
import { RouterModule } from '@angular/router';
================================================
FILE: packages/guess-parser/test/fixtures/angular/src/environments/environment.prod.ts
================================================
export const environment = {
production: true
};
================================================
FILE: packages/guess-parser/test/fixtures/angular/src/environments/environment.ts
================================================
// The file contents for the current environment will overwrite these during build.
// The build system defaults to the dev environment which uses `environment.ts`, but if you do
// `ng build --env=prod` then `environment.prod.ts` will be used instead.
// The list of which env maps to which file can be found in `.angular-cli.json`.
export const environment = {
production: false
};
================================================
FILE: packages/guess-parser/test/fixtures/angular/src/index.html
================================================
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Angular</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
<app-root></app-root>
</body>
</html>
================================================
FILE: packages/guess-parser/test/fixtures/angular/src/main.ts
================================================
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
if (environment.production) {
enableProdMode();
}
platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => console.log(err));
================================================
FILE: packages/guess-parser/test/fixtures/angular/src/polyfills.ts
================================================
/**
* This file includes polyfills needed by Angular and is loaded before the app.
* You can add your own extra polyfills to this file.
*
* This file is divided into 2 sections:
* 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
* 2. Application imports. Files imported after ZoneJS that should be loaded before your main
* file.
*
* The current setup is for so-called "evergreen" browsers; the last versions of browsers that
* automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
* Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
*
* Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html
*/
/***************************************************************************************************
* BROWSER POLYFILLS
*/
/** IE9, IE10 and IE11 requires all of the following polyfills. **/
// import 'core-js/es6/symbol';
// import 'core-js/es6/object';
// import 'core-js/es6/function';
// import 'core-js/es6/parse-int';
// import 'core-js/es6/parse-float';
// import 'core-js/es6/number';
// import 'core-js/es6/math';
// import 'core-js/es6/string';
// import 'core-js/es6/date';
// import 'core-js/es6/array';
// import 'core-js/es6/regexp';
// import 'core-js/es6/map';
// import 'core-js/es6/weak-map';
// import 'core-js/es6/set';
/** IE10 and IE11 requires the following for NgClass support on SVG elements */
// import 'classlist.js'; // Run `npm install --save classlist.js`.
/** IE10 and IE11 requires the following for the Reflect API. */
// import 'core-js/es6/reflect';
/** Evergreen browsers require these. **/
// Used for reflect-metadata in JIT. If you use AOT (and only Angular decorators), you can remove.
import 'core-js/es7/reflect';
/**
* Required to support Web Animations `@angular/platform-browser/animations`.
* Needed for: All but Chrome, Firefox and Opera. http://caniuse.com/#feat=web-animation
**/
// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
/**
* By default, zone.js will patch all possible macroTask and DomEvents
* user can disable parts of macroTask/DomEvents patch by setting following flags
*/
// (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
// (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
// (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
/*
* in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
* with the following flag, it will bypass `zone.js` patch for IE/Edge
*/
// (window as any).__Zone_enable_cross_context_check = true;
/***************************************************************************************************
* Zone JS is required by default for Angular itself.
*/
import 'zone.js/dist/zone'; // Included with Angular CLI.
/***************************************************************************************************
* APPLICATION IMPORTS
*/
================================================
FILE: packages/guess-parser/test/fixtures/angular/src/styles.css
================================================
/* You can add global styles to this file, and also import other style files */
================================================
FILE: packages/guess-parser/test/fixtures/angular/src/test.ts
================================================
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import 'zone.js/dist/zone-testing';
import { getTestBed } from '@angular/core/testing';
import {
BrowserDynamicTestingModule,
platformBrowserDynamicTesting
} from '@angular/platform-browser-dynamic/testing';
declare const require: any;
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting()
);
// Then we find all the tests.
const context = require.context('./', true, /\.spec\.ts$/);
// And load the modules.
context.keys().map(context);
================================================
FILE: packages/guess-parser/test/fixtures/angular/src/tsconfig.app.json
================================================
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/app",
"module": "es2015",
"types": []
},
"exclude": [
"test.ts",
"**/*.spec.ts"
]
}
================================================
FILE: packages/guess-parser/test/fixtures/angular/src/tsconfig.spec.json
================================================
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/spec",
"module": "commonjs",
"types": [
"jasmine",
"node"
]
},
"files": [
"test.ts"
],
"include": [
"**/*.spec.ts",
"**/*.d.ts"
]
}
================================================
FILE: packages/guess-parser/test/fixtures/angular/tsconfig.json
================================================
{
"compileOnSave": false,
"compilerOptions": {
"outDir": "./dist/out-tsc",
"sourceMap": true,
"declaration": false,
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"target": "es5",
"typeRoots": [
"node_modules/@types"
],
"lib": [
"es2017",
"dom"
],
"baseUrl": ".",
"paths": {
"~library": ["library/index.ts"],
}
}
}
================================================
FILE: packages/guess-parser/test/fixtures/gatsby/.gitignore
================================================
# Project dependencies
.cache
node_modules
yarn-error.log
# Build directory
/public
.DS_Store
================================================
FILE: packages/guess-parser/test/fixtures/gatsby/.prettierrc
================================================
{
"semi": false,
"singleQuote": true,
"trailingComma": "es5"
}
================================================
FILE: packages/guess-parser/test/fixtures/gatsby/LICENSE
================================================
The MIT License (MIT)
Copyright (c) 2015 gatsbyjs
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: packages/guess-parser/test/fixtures/gatsby/README.md
================================================
# gatsby-starter-default
The default Gatsby starter.
For an overview of the project structure please refer to the [Gatsby documentation - Building with Components](https://www.gatsbyjs.org/docs/building-with-components/).
## Install
Make sure that you have the Gatsby CLI program installed:
```sh
npm install --global gatsby-cli
```
And run from your CLI:
```sh
gatsby new gatsby-example-site
```
Then you can run it by:
```sh
cd gatsby-example-site
npm run develop
```
## Deploy
[](https://app.netlify.com/start/deploy?repository=https://github.com/gatsbyjs/gatsby-starter-default)
================================================
FILE: packages/guess-parser/test/fixtures/gatsby/gatsby-browser.js
================================================
/**
* Implement Gatsby's Browser APIs in this file.
*
* See: https://www.gatsbyjs.org/docs/browser-apis/
*/
// You can delete this file if you're not using it
================================================
FILE: packages/guess-parser/test/fixtures/gatsby/gatsby-config.js
================================================
module.exports = {
siteMetadata: {
title: 'Gatsby Default Starter',
},
plugins: ['gatsby-plugin-react-helmet'],
}
================================================
FILE: packages/guess-parser/test/fixtures/gatsby/gatsby-node.js
================================================
/**
* Implement Gatsby's Node APIs in this file.
*
* See: https://www.gatsbyjs.org/docs/node-apis/
*/
// You can delete this file if you're not using it
================================================
FILE: packages/guess-parser/test/fixtures/gatsby/gatsby-ssr.js
================================================
/**
* Implement Gatsby's SSR (Server Side Rendering) APIs in this file.
*
* See: https://www.gatsbyjs.org/docs/ssr-apis/
*/
// You can delete this file if you're not using it
================================================
FILE: packages/guess-parser/test/fixtures/gatsby/package.json
================================================
{
"name": "gatsby-starter-default",
"description": "Gatsby default starter",
"version": "1.0.0",
"author": "Kyle Mathews <mathews.kyle@gmail.com>",
"dependencies": {
"gatsby": "^1.9.247",
"gatsby-link": "^1.6.40",
"gatsby-plugin-react-helmet": "^2.0.10",
"react-helmet": "^5.2.0"
},
"keywords": [
"gatsby"
],
"license": "MIT",
"scripts": {
"build": "gatsby build",
"develop": "gatsby develop",
"format": "prettier --write 'src/**/*.js'",
"test": "echo \"Error: no test specified\" && exit 1"
},
"devDependencies": {
"prettier": "^1.12.0"
}
}
================================================
FILE: packages/guess-parser/test/fixtures/gatsby/src/components/header.js
================================================
import React from 'react'
import Link from 'gatsby-link'
const Header = ({ siteTitle }) => (
<div
style={{
background: 'rebeccapurple',
marginBottom: '1.45rem',
}}
>
<div
style={{
margin: '0 auto',
maxWidth: 960,
padding: '1.45rem 1.0875rem',
}}
>
<h1 style={{ margin: 0 }}>
<Link
to="/"
style={{
color: 'white',
textDecoration: 'none',
}}
>
{siteTitle}
</Link>
</h1>
</div>
</div>
)
export default Header
================================================
FILE: packages/guess-parser/test/fixtures/gatsby/src/layouts/index.css
================================================
html {
font-family: sans-serif;
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
}
body {
margin: 0;
}
article,
aside,
details,
figcaption,
figure,
footer,
header,
main,
menu,
nav,
section,
summary {
display: block;
}
audio,
canvas,
progress,
video {
display: inline-block;
}
audio:not([controls]) {
display: none;
height: 0;
}
progress {
vertical-align: baseline;
}
[hidden],
template {
display: none;
}
a {
background-color: transparent;
-webkit-text-decoration-skip: objects;
}
a:active,
a:hover {
outline-width: 0;
}
abbr[title] {
border-bottom: none;
text-decoration: underline;
text-decoration: underline dotted;
}
b,
strong {
font-weight: inherit;
font-weight: bolder;
}
dfn {
font-style: italic;
}
h1 {
font-size: 2em;
margin: .67em 0;
}
mark {
background-color: #ff0;
color: #000;
}
small {
font-size: 80%;
}
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sub {
bottom: -.25em;
}
sup {
top: -.5em;
}
img {
border-style: none;
}
svg:not(:root) {
overflow: hidden;
}
code,
kbd,
pre,
samp {
font-family: monospace, monospace;
font-size: 1em;
}
figure {
margin: 1em 40px;
}
hr {
box-sizing: content-box;
height: 0;
overflow: visible;
}
button,
input,
optgroup,
select,
textarea {
font: inherit;
margin: 0;
}
optgroup {
font-weight: 700;
}
button,
input {
overflow: visible;
}
button,
select {
text-transform: none;
}
[type=reset],
[type=submit],
button,
html [type=button] {
-webkit-appearance: button;
}
[type=button]::-moz-focus-inner,
[type=reset]::-moz-focus-inner,
[type=submit]::-moz-focus-inner,
button::-moz-focus-inner {
border-style: none;
padding: 0;
}
[type=button]:-moz-focusring,
[type=reset]:-moz-focusring,
[type=submit]:-moz-focusring,
button:-moz-focusring {
outline: 1px dotted ButtonText;
}
fieldset {
border: 1px solid silver;
margin: 0 2px;
padding: .35em .625em .75em;
}
legend {
box-sizing: border-box;
color: inherit;
display: table;
max-width: 100%;
padding: 0;
white-space: normal;
}
textarea {
overflow: auto;
}
[type=checkbox],
[type=radio] {
box-sizing: border-box;
padding: 0;
}
[type=number]::-webkit-inner-spin-button,
[type=number]::-webkit-outer-spin-button {
height: auto;
}
[type=search] {
-webkit-appearance: textfield;
outline-offset: -2px;
}
[type=search]::-webkit-search-cancel-button,
[type=search]::-webkit-search-decoration {
-webkit-appearance: none;
}
::-webkit-input-placeholder {
color: inherit;
opacity: .54;
}
::-webkit-file-upload-button {
-webkit-appearance: button;
font: inherit;
}
html {
font: 112.5%/1.45em georgia, serif;
box-sizing: border-box;
overflow-y: scroll;
}
* {
box-sizing: inherit;
}
*:before {
box-sizing: inherit;
}
*:after {
box-sizing: inherit;
}
body {
color: hsla(0, 0%, 0%, 0.8);
font-family: georgia, serif;
font-weight: normal;
word-wrap: break-word;
font-kerning: normal;
-moz-font-feature-settings: "kern", "liga", "clig", "calt";
-ms-font-feature-settings: "kern", "liga", "clig", "calt";
-webkit-font-feature-settings: "kern", "liga", "clig", "calt";
font-feature-settings: "kern", "liga", "clig", "calt";
}
img {
max-width: 100%;
margin-left: 0;
margin-right: 0;
margin-top: 0;
padding-bottom: 0;
padding-left: 0;
padding-right: 0;
padding-top: 0;
margin-bottom: 1.45rem;
}
h1 {
margin-left: 0;
margin-right: 0;
margin-top: 0;
padding-bottom: 0;
padding-left: 0;
padding-right: 0;
padding-top: 0;
margin-bottom: 1.45rem;
color: inherit;
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
font-weight: bold;
text-rendering: optimizeLegibility;
font-size: 2.25rem;
line-height: 1.1;
}
h2 {
margin-left: 0;
margin-right: 0;
margin-top: 0;
padding-bottom: 0;
padding-left: 0;
padding-right: 0;
padding-top: 0;
margin-bottom: 1.45rem;
color: inherit;
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
font-weight: bold;
text-rendering: optimizeLegibility;
font-size: 1.62671rem;
line-height: 1.1;
}
h3 {
margin-left: 0;
margin-right: 0;
margin-top: 0;
padding-bottom: 0;
padding-left: 0;
padding-right: 0;
padding-top: 0;
margin-bottom: 1.45rem;
color: inherit;
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
font-weight: bold;
text-rendering: optimizeLegibility;
font-size: 1.38316rem;
line-height: 1.1;
}
h4 {
margin-left: 0;
margin-right: 0;
margin-top: 0;
padding-bottom: 0;
padding-left: 0;
padding-right: 0;
padding-top: 0;
margin-bottom: 1.45rem;
color: inherit;
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
font-weight: bold;
text-rendering: optimizeLegibility;
font-size: 1rem;
line-height: 1.1;
}
h5 {
margin-left: 0;
margin-right: 0;
margin-top: 0;
padding-bottom: 0;
padding-left: 0;
padding-right: 0;
padding-top: 0;
margin-bottom: 1.45rem;
color: inherit;
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
font-weight: bold;
text-rendering: optimizeLegibility;
font-size: 0.85028rem;
line-height: 1.1;
}
h6 {
margin-left: 0;
margin-right: 0;
margin-top: 0;
padding-bottom: 0;
padding-left: 0;
padding-right: 0;
padding-top: 0;
margin-bottom: 1.45rem;
color: inherit;
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
font-weight: bold;
text-rendering: optimizeLegibility;
font-size: 0.78405rem;
line-height: 1.1;
}
hgroup {
margin-left: 0;
margin-right: 0;
margin-top: 0;
padding-bottom: 0;
padding-left: 0;
padding-right: 0;
padding-top: 0;
margin-bottom: 1.45rem;
}
ul {
margin-left: 1.45rem;
margin-right: 0;
margin-top: 0;
padding-bottom: 0;
padding-left: 0;
padding-right: 0;
padding-top: 0;
margin-bottom: 1.45rem;
list-style-position: outside;
list-style-image: none;
}
ol {
margin-left: 1.45rem;
margin-right: 0;
margin-top: 0;
padding-bottom: 0;
padding-left: 0;
padding-right: 0;
padding-top: 0;
margin-bottom: 1.45rem;
list-style-position: outside;
list-style-image: none;
}
dl {
margin-left: 0;
margin-right: 0;
margin-top: 0;
padding-bottom: 0;
padding-left: 0;
padding-right: 0;
padding-top: 0;
margin-bottom: 1.45rem;
}
dd {
margin-left: 0;
margin-right: 0;
margin-top: 0;
padding-bottom: 0;
padding-left: 0;
padding-right: 0;
padding-top: 0;
margin-bottom: 1.45rem;
}
p {
margin-left: 0;
margin-right: 0;
margin-top: 0;
padding-bottom: 0;
padding-left: 0;
padding-right: 0;
padding-top: 0;
margin-bottom: 1.45rem;
}
figure {
margin-left: 0;
margin-right: 0;
margin-top: 0;
padding-bottom: 0;
padding-left: 0;
padding-right: 0;
padding-top: 0;
margin-bottom: 1.45rem;
}
pre {
margin-left: 0;
margin-right: 0;
margin-top: 0;
padding-bottom: 0;
padding-left: 0;
padding-right: 0;
padding-top: 0;
margin-bottom: 1.45rem;
font-size: 0.85rem;
line-height: 1.42;
background: hsla(0, 0%, 0%, 0.04);
border-radius: 3px;
overflow: auto;
word-wrap: normal;
padding: 1.45rem;
}
table {
margin-left: 0;
margin-right: 0;
margin-top: 0;
padding-bottom: 0;
padding-left: 0;
padding-right: 0;
padding-top: 0;
margin-bottom: 1.45rem;
font-size: 1rem;
line-height: 1.45rem;
border-collapse: collapse;
width: 100%;
}
fieldset {
margin-left: 0;
margin-right: 0;
margin-top: 0;
padding-bottom: 0;
padding-left: 0;
padding-right: 0;
padding-top: 0;
margin-bottom: 1.45rem;
}
blockquote {
margin-left: 1.45rem;
margin-right: 1.45rem;
margin-top: 0;
padding-bottom: 0;
padding-left: 0;
padding-right: 0;
padding-top: 0;
margin-bottom: 1.45rem;
}
form {
margin-left: 0;
margin-right: 0;
margin-top: 0;
padding-bottom: 0;
padding-left: 0;
padding-right: 0;
padding-top: 0;
margin-bottom: 1.45rem;
}
noscript {
margin-left: 0;
margin-right: 0;
margin-top: 0;
padding-bottom: 0;
padding-left: 0;
padding-right: 0;
padding-top: 0;
margin-bottom: 1.45rem;
}
iframe {
margin-left: 0;
margin-right: 0;
margin-top: 0;
padding-bottom: 0;
padding-left: 0;
padding-right: 0;
padding-top: 0;
margin-bottom: 1.45rem;
}
hr {
margin-left: 0;
margin-right: 0;
margin-top: 0;
padding-bottom: 0;
padding-left: 0;
padding-right: 0;
padding-top: 0;
margin-bottom: calc(1.45rem - 1px);
background: hsla(0, 0%, 0%, 0.2);
border: none;
height: 1px;
}
address {
margin-left: 0;
margin-right: 0;
margin-top: 0;
padding-bottom: 0;
padding-left: 0;
padding-right: 0;
padding-top: 0;
margin-bottom: 1.45rem;
}
b {
font-weight: bold;
}
strong {
font-weight: bold;
}
dt {
font-weight: bold;
}
th {
font-weight: bold;
}
li {
margin-bottom: calc(1.45rem / 2);
}
ol li {
padding-left: 0;
}
ul li {
padding-left: 0;
}
li > ol {
margin-left: 1.45rem;
margin-bottom: calc(1.45rem / 2);
margin-top: calc(1.45rem / 2);
}
li > ul {
margin-left: 1.45rem;
margin-bottom: calc(1.45rem / 2);
margin-top: calc(1.45rem / 2);
}
blockquote *:last-child {
margin-bottom: 0;
}
li *:last-child {
margin-bottom: 0;
}
p *:last-child {
margin-bottom: 0;
}
li > p {
margin-bottom: calc(1.45rem / 2);
}
code {
font-size: 0.85rem;
line-height: 1.45rem;
}
kbd {
font-size: 0.85rem;
line-height: 1.45rem;
}
samp {
font-size: 0.85rem;
line-height: 1.45rem;
}
abbr {
border-bottom: 1px dotted hsla(0, 0%, 0%, 0.5);
cursor: help;
}
acronym {
border-bottom: 1px dotted hsla(0, 0%, 0%, 0.5);
cursor: help;
}
abbr[title] {
border-bottom: 1px dotted hsla(0, 0%, 0%, 0.5);
cursor: help;
text-decoration: none;
}
thead {
text-align: left;
}
td,
th {
text-align: left;
border-bottom: 1px solid hsla(0, 0%, 0%, 0.12);
font-feature-settings: "tnum";
-moz-font-feature-settings: "tnum";
-ms-font-feature-settings: "tnum";
-webkit-font-feature-settings: "tnum";
padding-left: 0.96667rem;
padding-right: 0.96667rem;
padding-top: 0.725rem;
padding-bottom: calc(0.725rem - 1px);
}
th:first-child,
td:first-child {
padding-left: 0;
}
th:last-child,
td:last-child {
padding-right: 0;
}
tt,
code {
background-color: hsla(0, 0%, 0%, 0.04);
border-radius: 3px;
font-family: "SFMono-Regular", Consolas, "Roboto Mono", "Droid Sans Mono",
"Liberation Mono", Menlo, Courier, monospace;
padding: 0;
padding-top: 0.2em;
padding-bottom: 0.2em;
}
pre code {
background: none;
line-height: 1.42;
}
code:before,
code:after,
tt:before,
tt:after {
letter-spacing: -0.2em;
content: " ";
}
pre code:before,
pre code:after,
pre tt:before,
pre tt:after {
content: "";
}
@media only screen and (max-width: 480px) {
html {
font-size: 100%;
}
}
================================================
FILE: packages/guess-parser/test/fixtures/gatsby/src/layouts/index.js
================================================
import React from 'react'
import PropTypes from 'prop-types'
import Helmet from 'react-helmet'
import Header from '../components/header'
import './index.css'
const Layout = ({ children, data }) => (
<div>
<Helmet
title={data.site.siteMetadata.title}
meta={[
{ name: 'description', content: 'Sample' },
{ name: 'keywords', content: 'sample, something' },
]}
/>
<Header siteTitle={data.site.siteMetadata.title} />
<div
style={{
margin: '0 auto',
maxWidth: 960,
padding: '0px 1.0875rem 1.45rem',
paddingTop: 0,
}}
>
{children()}
</div>
</div>
)
Layout.propTypes = {
children: PropTypes.func,
}
export default Layout
export const query = graphql`
query SiteTitleQuery {
site {
siteMetadata {
title
}
}
}
`
================================================
FILE: packages/guess-parser/test/fixtures/gatsby/src/pages/404.js
================================================
import React from 'react'
const NotFoundPage = () => (
<div>
<h1>NOT FOUND</h1>
<p>You just hit a route that doesn't exist... the sadness.</p>
</div>
)
export default NotFoundPage
================================================
FILE: packages/guess-parser/test/fixtures/gatsby/src/pages/index.js
================================================
import React from 'react'
import Link from 'gatsby-link'
const IndexPage = () => (
<div>
<h1>Hi people</h1>
<p>Welcome to your new Gatsby site.</p>
<p>Now go build something great.</p>
<Link to="/page-2/">Go to page 2</Link>
</div>
)
export default IndexPage
================================================
FILE: packages/guess-parser/test/fixtures/gatsby/src/pages/page-2.js
================================================
import React from 'react'
import Link from 'gatsby-link'
const SecondPage = () => (
<div>
<h1>Hi from the second page</h1>
<p>Welcome to page 2</p>
<Link to="/">Go back to the homepage</Link>
</div>
)
export default SecondPage
================================================
FILE: packages/guess-parser/test/fixtures/ng8/.editorconfig
================================================
# Editor configuration, see https://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
max_line_length = off
trim_trailing_whitespace = false
================================================
FILE: packages/guess-parser/test/fixtures/ng8/.gitignore
================================================
# See http://help.github.com/ignore-files/ for more about ignoring files.
# compiled output
/dist
/tmp
/out-tsc
# Only exists if Bazel was run
/bazel-out
# dependencies
/node_modules
# profiling files
chrome-profiler-events.json
speed-measure-plugin.json
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
.history/*
# misc
/.sass-cache
/connect.lock
/coverage
/libpeerconnection.log
npm-debug.log
yarn-error.log
testem.log
/typings
# System Files
.DS_Store
Thumbs.db
================================================
FILE: packages/guess-parser/test/fixtures/ng8/README.md
================================================
# Ng8
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 8.0.1.
## Development server
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
## Code scaffolding
Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
## Build
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build.
## Running unit tests
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
## Running end-to-end tests
Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
## Further help
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).
================================================
FILE: packages/guess-parser/test/fixtures/ng8/angular.json
================================================
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"ng8": {
"projectType": "application",
"schematics": {},
"root": "",
"sourceRoot": "src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist/ng8",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.app.json",
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"src/styles.css"
],
"scripts": []
},
"configurations": {
"production": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"extractCss": true,
"namedChunks": false,
"aot": true,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"budgets": [
{
"type": "initial",
"maximumWarning": "2mb",
"maximumError": "5mb"
}
]
}
}
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "ng8:build"
},
"configurations": {
"production": {
"browserTarget": "ng8:build:production"
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "ng8:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "src/test.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.spec.json",
"karmaConfig": "karma.conf.js",
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"src/styles.css"
],
"scripts": []
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"tsconfig.app.json",
"tsconfig.spec.json",
"e2e/tsconfig.json"
],
"exclude": [
"**/node_modules/**"
]
}
},
"e2e": {
"builder": "@angular-devkit/build-angular:protractor",
"options": {
"protractorConfig": "e2e/protractor.conf.js",
"devServerTarget": "ng8:serve"
},
"configurations": {
"production": {
"devServerTarget": "ng8:serve:production"
}
}
}
}
}},
"defaultProject": "ng8"
}
================================================
FILE: packages/guess-parser/test/fixtures/ng8/browserslist
================================================
# This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
# For additional information regarding the format and rule options, please see:
# https://github.com/browserslist/browserslist#queries
# You can see what browsers were selected by your queries by running:
# npx browserslist
> 0.5%
last 2 versions
Firefox ESR
not dead
not IE 9-11 # For IE 9-11 support, remove 'not'.
================================================
FILE: packages/guess-parser/test/fixtures/ng8/e2e/protractor.conf.js
================================================
// @ts-check
// Protractor configuration file, see link for more information
// https://github.com/angular/protractor/blob/master/lib/config.ts
const { SpecReporter } = require('jasmine-spec-reporter');
/**
* @type { import("protractor").Config }
*/
exports.config = {
allScriptsTimeout: 11000,
specs: [
'./src/**/*.e2e-spec.ts'
],
capabilities: {
'browserName': 'chrome'
},
directConnect: true,
baseUrl: 'http://localhost:4200/',
framework: 'jasmine',
jasmineNodeOpts: {
showColors: true,
defaultTimeoutInterval: 30000,
print: function() {}
},
onPrepare() {
require('ts-node').register({
project: require('path').join(__dirname, './tsconfig.json')
});
jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
}
};
================================================
FILE: packages/guess-parser/test/fixtures/ng8/e2e/src/app.e2e-spec.ts
================================================
import { AppPage } from './app.po';
import { browser, logging } from 'protractor';
describe('workspace-project App', () => {
let page: AppPage;
beforeEach(() => {
page = new AppPage();
});
it('should display welcome message', () => {
page.navigateTo();
expect(page.getTitleText()).toEqual('Welcome to ng8!');
});
afterEach(async () => {
// Assert that there are no errors emitted from the browser
const logs = await browser.manage().logs().get(logging.Type.BROWSER);
expect(logs).not.toContain(jasmine.objectContaining({
level: logging.Level.SEVERE,
} as logging.Entry));
});
});
================================================
FILE: packages/guess-parser/test/fixtures/ng8/e2e/src/app.po.ts
================================================
import { browser, by, element } from 'protractor';
export class AppPage {
navigateTo() {
return browser.get(browser.baseUrl) as Promise<any>;
}
getTitleText() {
return element(by.css('app-root h1')).getText() as Promise<string>;
}
}
================================================
FILE: packages/guess-parser/test/fixtures/ng8/e2e/tsconfig.json
================================================
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/e2e",
"module": "commonjs",
"target": "es5",
"types": [
"jasmine",
"jasminewd2",
"node"
]
}
}
================================================
FILE: packages/guess-parser/test/fixtures/ng8/karma.conf.js
================================================
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter'),
require('@angular-devkit/build-angular/plugins/karma')
],
client: {
clearContext: false // leave Jasmine Spec Runner output visible in browser
},
coverageIstanbulReporter: {
dir: require('path').join(__dirname, './coverage/ng8'),
reports: ['html', 'lcovonly', 'text-summary'],
fixWebpackSourcePaths: true
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false,
restartOnFileChange: true
});
};
================================================
FILE: packages/guess-parser/test/fixtures/ng8/package.json
================================================
{
"name": "ng8",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
},
"private": true,
"dependencies": {
"@angular/animations": "~8.0.0",
"@angular/common": "~8.0.0",
"@angular/compiler": "~8.0.0",
"@angular/core": "~8.0.0",
"@angular/forms": "~8.0.0",
"@angular/platform-browser": "~8.0.0",
"@angular/platform-browser-dynamic": "~8.0.0",
"@angular/router": "~8.0.0",
"rxjs": "~6.4.0",
"tslib": "^1.9.0",
"zone.js": "~0.9.1"
},
"devDependencies": {
"@angular-devkit/build-angular": "~0.800.0",
"@angular/cli": "~8.0.1",
"@angular/compiler-cli": "~8.0.0",
"@angular/language-service": "~8.0.0",
"@types/node": "~8.9.4",
"@types/jasmine": "~3.3.8",
"@types/jasminewd2": "~2.0.3",
"codelyzer": "^5.0.0",
"jasmine-core": "~3.4.0",
"jasmine-spec-reporter": "~4.2.1",
"karma": "~4.1.0",
"karma-chrome-launcher": "~2.2.0",
"karma-coverage-istanbul-reporter": "~2.0.1",
"karma-jasmine": "~2.0.1",
"karma-jasmine-html-reporter": "^1.4.0",
"protractor": "~5.4.0",
"ts-node": "~7.0.0",
"tslint": "~5.15.0",
"typescript": "~3.4.3"
}
}
================================================
FILE: packages/guess-parser/test/fixtures/ng8/src/app/app-routing.module.ts
================================================
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
const routes: Routes = [];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
================================================
FILE: packages/guess-parser/test/fixtures/ng8/src/app/app.component.css
================================================
================================================
FILE: packages/guess-parser/test/fixtures/ng8/src/app/app.component.html
================================================
<!--The content below is only a placeholder and can be replaced.-->
<div style="text-align:center">
<h1>
Welcome to {{ title }}!
</h1>
<img width="300" alt="Angular Logo" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTAgMjUwIj4KICAgIDxwYXRoIGZpbGw9IiNERDAwMzEiIGQ9Ik0xMjUgMzBMMzEuOSA2My4ybDE0LjIgMTIzLjFMMTI1IDIzMGw3OC45LTQzLjcgMTQuMi0xMjMuMXoiIC8+CiAgICA8cGF0aCBmaWxsPSIjQzMwMDJGIiBkPSJNMTI1IDMwdjIyLjItLjFWMjMwbDc4LjktNDMuNyAxNC4yLTEyMy4xTDEyNSAzMHoiIC8+CiAgICA8cGF0aCAgZmlsbD0iI0ZGRkZGRiIgZD0iTTEyNSA1Mi4xTDY2LjggMTgyLjZoMjEuN2wxMS43LTI5LjJoNDkuNGwxMS43IDI5LjJIMTgzTDEyNSA1Mi4xem0xNyA4My4zaC0zNGwxNy00MC45IDE3IDQwLjl6IiAvPgogIDwvc3ZnPg==">
</div>
<h2>Here are some links to help you start: </h2>
<ul>
<li>
<h2><a target="_blank" rel="noopener" href="https://angular.io/tutorial">Tour of Heroes</a></h2>
</li>
<li>
<h2><a target="_blank" rel="noopener" href="https://angular.io/cli">CLI Documentation</a></h2>
</li>
<li>
<h2><a target="_blank" rel="noopener" href="https://blog.angular.io/">Angular blog</a></h2>
</li>
</ul>
<router-outlet></router-outlet>
================================================
FILE: packages/guess-parser/test/fixtures/ng8/src/app/app.component.spec.ts
================================================
import { TestBed, async } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { AppComponent } from './app.component';
describe('AppComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
RouterTestingModule
],
declarations: [
AppComponent
],
}).compileComponents();
}));
it('should create the app', () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance;
expect(app).toBeTruthy();
});
it(`should have as title 'ng8'`, () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance;
expect(app.title).toEqual('ng8');
});
it('should render title in a h1 tag', () => {
const fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges();
const compiled = fixture.debugElement.nativeElement;
expect(compiled.querySelector('h1').textContent).toContain('Welcome to ng8!');
});
});
================================================
FILE: packages/guess-parser/test/fixtures/ng8/src/app/app.component.ts
================================================
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'ng8';
}
================================================
FILE: packages/guess-parser/test/fixtures/ng8/src/app/app.module.ts
================================================
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './
gitextract_rzzsvapf/ ├── .gitignore ├── .travis.yml ├── CONTRIBUTING.md ├── DEVELOPING.md ├── LICENSE ├── README.md ├── experiments/ │ └── guess-static-sites/ │ ├── .gitignore │ ├── README.md │ ├── config.js │ ├── generatePredictions.js │ ├── package.json │ ├── predictiveFetching.js │ ├── server.js │ ├── src/ │ │ ├── models/ │ │ │ ├── pageView.js │ │ │ └── prediction.js │ │ ├── parser.js │ │ └── queryParams.js │ └── test/ │ ├── clientTests.js │ ├── fixtures/ │ │ ├── gaResponse.json │ │ ├── server.js │ │ └── test.html │ ├── gaClientTests.js │ └── serverTests.js ├── infra/ │ ├── e2e.ts │ ├── install.ts │ ├── pretest.ts │ └── test.ts ├── jest-puppeteer.config.js ├── jest.config.js ├── lerna.json ├── package.json ├── packages/ │ ├── common/ │ │ ├── interfaces.ts │ │ └── logger.ts │ ├── guess-ga/ │ │ ├── .npmignore │ │ ├── README.md │ │ ├── index.ts │ │ ├── package.json │ │ ├── src/ │ │ │ ├── client.ts │ │ │ ├── ga.ts │ │ │ └── normalize.ts │ │ ├── test/ │ │ │ └── normalize.spec.ts │ │ ├── tsconfig.json │ │ └── webpack.config.js │ ├── guess-parser/ │ │ ├── .npmignore │ │ ├── README.md │ │ ├── index.ts │ │ ├── package.json │ │ ├── src/ │ │ │ ├── angular/ │ │ │ │ ├── index.ts │ │ │ │ ├── modules.ts │ │ │ │ ├── route-parser.ts │ │ │ │ └── routes.ts │ │ │ ├── detector/ │ │ │ │ ├── detect.ts │ │ │ │ └── index.ts │ │ │ ├── language-service.ts │ │ │ ├── parser.ts │ │ │ ├── preact/ │ │ │ │ └── index.ts │ │ │ ├── react/ │ │ │ │ ├── base.ts │ │ │ │ ├── index.ts │ │ │ │ ├── react-jsx.ts │ │ │ │ └── react-tsx.ts │ │ │ └── utils.ts │ │ ├── test/ │ │ │ ├── angular.spec.ts │ │ │ ├── detect.spec.ts │ │ │ ├── fixtures/ │ │ │ │ ├── angular/ │ │ │ │ │ ├── .angular-cli.json │ │ │ │ │ ├── library/ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── library.module.ts │ │ │ │ │ │ ├── nested/ │ │ │ │ │ │ │ └── library.module.ts │ │ │ │ │ │ └── tsconfig.json │ │ │ │ │ ├── package.json │ │ │ │ │ ├── src/ │ │ │ │ │ │ ├── app/ │ │ │ │ │ │ │ ├── about/ │ │ │ │ │ │ │ │ ├── about-routing.module.ts │ │ │ │ │ │ │ │ └── about.module.ts │ │ │ │ │ │ │ ├── app-routing.module.ts │ │ │ │ │ │ │ ├── app.component.css │ │ │ │ │ │ │ ├── app.component.html │ │ │ │ │ │ │ ├── app.component.spec.ts │ │ │ │ │ │ │ ├── app.component.ts │ │ │ │ │ │ │ ├── app.module.ts │ │ │ │ │ │ │ ├── bar/ │ │ │ │ │ │ │ │ ├── bar.module.ts │ │ │ │ │ │ │ │ └── baz/ │ │ │ │ │ │ │ │ ├── baz.module.ts │ │ │ │ │ │ │ │ └── cycle-parent.ts │ │ │ │ │ │ │ ├── bar-simple.component.ts │ │ │ │ │ │ │ ├── foo/ │ │ │ │ │ │ │ │ ├── baz/ │ │ │ │ │ │ │ │ │ ├── baz-routing.module.ts │ │ │ │ │ │ │ │ │ ├── baz.component.ts │ │ │ │ │ │ │ │ │ └── baz.module.ts │ │ │ │ │ │ │ │ ├── foo-routing.module.ts │ │ │ │ │ │ │ │ ├── foo.component.ts │ │ │ │ │ │ │ │ └── foo.module.ts │ │ │ │ │ │ │ ├── lazy/ │ │ │ │ │ │ │ │ ├── lazy-routing.module.ts │ │ │ │ │ │ │ │ ├── lazy.component.ts │ │ │ │ │ │ │ │ └── lazy.module.ts │ │ │ │ │ │ │ ├── qux/ │ │ │ │ │ │ │ │ └── qux.module.ts │ │ │ │ │ │ │ └── wrapper/ │ │ │ │ │ │ │ └── wrapper.module.ts │ │ │ │ │ │ ├── environments/ │ │ │ │ │ │ │ ├── environment.prod.ts │ │ │ │ │ │ │ └── environment.ts │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ ├── main.ts │ │ │ │ │ │ ├── polyfills.ts │ │ │ │ │ │ ├── styles.css │ │ │ │ │ │ ├── test.ts │ │ │ │ │ │ ├── tsconfig.app.json │ │ │ │ │ │ └── tsconfig.spec.json │ │ │ │ │ └── tsconfig.json │ │ │ │ ├── gatsby/ │ │ │ │ │ ├── .gitignore │ │ │ │ │ ├── .prettierrc │ │ │ │ │ ├── LICENSE │ │ │ │ │ ├── README.md │ │ │ │ │ ├── gatsby-browser.js │ │ │ │ │ ├── gatsby-config.js │ │ │ │ │ ├── gatsby-node.js │ │ │ │ │ ├── gatsby-ssr.js │ │ │ │ │ ├── package.json │ │ │ │ │ └── src/ │ │ │ │ │ ├── components/ │ │ │ │ │ │ └── header.js │ │ │ │ │ ├── layouts/ │ │ │ │ │ │ ├── index.css │ │ │ │ │ │ └── index.js │ │ │ │ │ └── pages/ │ │ │ │ │ ├── 404.js │ │ │ │ │ ├── index.js │ │ │ │ │ └── page-2.js │ │ │ │ ├── ng8/ │ │ │ │ │ ├── .editorconfig │ │ │ │ │ ├── .gitignore │ │ │ │ │ ├── README.md │ │ │ │ │ ├── angular.json │ │ │ │ │ ├── browserslist │ │ │ │ │ ├── e2e/ │ │ │ │ │ │ ├── protractor.conf.js │ │ │ │ │ │ ├── src/ │ │ │ │ │ │ │ ├── app.e2e-spec.ts │ │ │ │ │ │ │ └── app.po.ts │ │ │ │ │ │ └── tsconfig.json │ │ │ │ │ ├── karma.conf.js │ │ │ │ │ ├── package.json │ │ │ │ │ ├── src/ │ │ │ │ │ │ ├── app/ │ │ │ │ │ │ │ ├── app-routing.module.ts │ │ │ │ │ │ │ ├── app.component.css │ │ │ │ │ │ │ ├── app.component.html │ │ │ │ │ │ │ ├── app.component.spec.ts │ │ │ │ │ │ │ ├── app.component.ts │ │ │ │ │ │ │ └── app.module.ts │ │ │ │ │ │ ├── assets/ │ │ │ │ │ │ │ └── .gitkeep │ │ │ │ │ │ ├── environments/ │ │ │ │ │ │ │ ├── environment.prod.ts │ │ │ │ │ │ │ └── environment.ts │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ ├── main.ts │ │ │ │ │ │ ├── polyfills.ts │ │ │ │ │ │ ├── styles.css │ │ │ │ │ │ └── test.ts │ │ │ │ │ ├── tsconfig.app.json │ │ │ │ │ ├── tsconfig.json │ │ │ │ │ ├── tsconfig.spec.json │ │ │ │ │ └── tslint.json │ │ │ │ ├── nx/ │ │ │ │ │ ├── LICENSE │ │ │ │ │ ├── README.md │ │ │ │ │ ├── angular.json │ │ │ │ │ ├── apps/ │ │ │ │ │ │ ├── customers-ui-e2e/ │ │ │ │ │ │ │ └── src/ │ │ │ │ │ │ │ └── integration/ │ │ │ │ │ │ │ └── customers.component.spec.ts │ │ │ │ │ │ ├── feat-home-e2e/ │ │ │ │ │ │ │ ├── cypress.json │ │ │ │ │ │ │ ├── src/ │ │ │ │ │ │ │ │ ├── fixtures/ │ │ │ │ │ │ │ │ │ └── example.json │ │ │ │ │ │ │ │ ├── integration/ │ │ │ │ │ │ │ │ │ └── home.component.spec.ts │ │ │ │ │ │ │ │ ├── plugins/ │ │ │ │ │ │ │ │ │ └── index.js │ │ │ │ │ │ │ │ └── support/ │ │ │ │ │ │ │ │ ├── commands.ts │ │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ │ ├── tsconfig.e2e.json │ │ │ │ │ │ │ ├── tsconfig.json │ │ │ │ │ │ │ └── tslint.json │ │ │ │ │ │ ├── ng-cli-app/ │ │ │ │ │ │ │ ├── browserslist │ │ │ │ │ │ │ ├── karma.conf.js │ │ │ │ │ │ │ ├── src/ │ │ │ │ │ │ │ │ ├── app/ │ │ │ │ │ │ │ │ │ ├── app.component.html │ │ │ │ │ │ │ │ │ ├── app.component.scss │ │ │ │ │ │ │ │ │ ├── app.component.spec.ts │ │ │ │ │ │ │ │ │ ├── app.component.ts │ │ │ │ │ │ │ │ │ └── app.module.ts │ │ │ │ │ │ │ │ ├── assets/ │ │ │ │ │ │ │ │ │ ├── .gitkeep │ │ │ │ │ │ │ │ │ └── customers.json │ │ │ │ │ │ │ │ ├── environments/ │ │ │ │ │ │ │ │ │ ├── environment.prod.ts │ │ │ │ │ │ │ │ │ └── environment.ts │ │ │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ │ │ ├── main.ts │ │ │ │ │ │ │ │ ├── polyfills.ts │ │ │ │ │ │ │ │ ├── styles.scss │ │ │ │ │ │ │ │ └── test.ts │ │ │ │ │ │ │ ├── tsconfig.app.json │ │ │ │ │ │ │ └── tsconfig.spec.json │ │ │ │ │ │ ├── ng-cli-app-e2e/ │ │ │ │ │ │ │ ├── cypress.json │ │ │ │ │ │ │ ├── src/ │ │ │ │ │ │ │ │ ├── fixtures/ │ │ │ │ │ │ │ │ │ └── example.json │ │ │ │ │ │ │ │ ├── integration/ │ │ │ │ │ │ │ │ │ ├── app.spec.ts │ │ │ │ │ │ │ │ │ ├── customers.spec.ts │ │ │ │ │ │ │ │ │ └── home.spec.ts │ │ │ │ │ │ │ │ ├── plugins/ │ │ │ │ │ │ │ │ │ └── index.js │ │ │ │ │ │ │ │ └── support/ │ │ │ │ │ │ │ │ ├── app.po.ts │ │ │ │ │ │ │ │ ├── commands.ts │ │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ │ ├── tsconfig.e2e.json │ │ │ │ │ │ │ └── tsconfig.json │ │ │ │ │ │ └── shared-components-e2e/ │ │ │ │ │ │ ├── cypress.json │ │ │ │ │ │ ├── src/ │ │ │ │ │ │ │ ├── fixtures/ │ │ │ │ │ │ │ │ └── example.json │ │ │ │ │ │ │ ├── integration/ │ │ │ │ │ │ │ │ ├── info-box/ │ │ │ │ │ │ │ │ │ └── info-box.component.spec.ts │ │ │ │ │ │ │ │ └── navigation/ │ │ │ │ │ │ │ │ └── navigation.component.spec.ts │ │ │ │ │ │ │ ├── plugins/ │ │ │ │ │ │ │ │ └── index.js │ │ │ │ │ │ │ └── support/ │ │ │ │ │ │ │ ├── commands.ts │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ ├── tsconfig.e2e.json │ │ │ │ │ │ ├── tsconfig.json │ │ │ │ │ │ └── tslint.json │ │ │ │ │ ├── docs/ │ │ │ │ │ │ ├── _config.yml │ │ │ │ │ │ └── index.md │ │ │ │ │ ├── index.js │ │ │ │ │ ├── jest.config.js │ │ │ │ │ ├── karma.conf.js │ │ │ │ │ ├── libs/ │ │ │ │ │ │ ├── .gitkeep │ │ │ │ │ │ ├── auth/ │ │ │ │ │ │ │ ├── README.md │ │ │ │ │ │ │ ├── jest.config.js │ │ │ │ │ │ │ ├── src/ │ │ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ │ │ ├── lib/ │ │ │ │ │ │ │ │ │ ├── auth-routing.module.ts │ │ │ │ │ │ │ │ │ ├── auth.guard.spec.ts │ │ │ │ │ │ │ │ │ ├── auth.guard.ts │ │ │ │ │ │ │ │ │ ├── auth.module.spec.ts │ │ │ │ │ │ │ │ │ ├── auth.module.ts │ │ │ │ │ │ │ │ │ ├── auth.service.spec.ts │ │ │ │ │ │ │ │ │ ├── auth.service.ts │ │ │ │ │ │ │ │ │ └── login/ │ │ │ │ │ │ │ │ │ ├── login.component.html │ │ │ │ │ │ │ │ │ ├── login.component.scss │ │ │ │ │ │ │ │ │ ├── login.component.spec.ts │ │ │ │ │ │ │ │ │ └── login.component.ts │ │ │ │ │ │ │ │ └── test-setup.ts │ │ │ │ │ │ │ ├── tsconfig.json │ │ │ │ │ │ │ ├── tsconfig.lib.json │ │ │ │ │ │ │ ├── tsconfig.spec.json │ │ │ │ │ │ │ └── tslint.json │ │ │ │ │ │ ├── customers/ │ │ │ │ │ │ │ ├── data/ │ │ │ │ │ │ │ │ ├── README.md │ │ │ │ │ │ │ │ ├── jest.config.js │ │ │ │ │ │ │ │ ├── src/ │ │ │ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ │ │ │ ├── lib/ │ │ │ │ │ │ │ │ │ │ ├── customer.model.ts │ │ │ │ │ │ │ │ │ │ ├── customer.service.spec.ts │ │ │ │ │ │ │ │ │ │ └── customer.service.ts │ │ │ │ │ │ │ │ │ └── test-setup.ts │ │ │ │ │ │ │ │ ├── tsconfig.json │ │ │ │ │ │ │ │ ├── tsconfig.lib.json │ │ │ │ │ │ │ │ ├── tsconfig.spec.json │ │ │ │ │ │ │ │ └── tslint.json │ │ │ │ │ │ │ └── ui/ │ │ │ │ │ │ │ ├── README.md │ │ │ │ │ │ │ ├── jest.config.js │ │ │ │ │ │ │ ├── src/ │ │ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ │ │ ├── lib/ │ │ │ │ │ │ │ │ │ ├── customer-list/ │ │ │ │ │ │ │ │ │ │ ├── customer-list-datasource.ts │ │ │ │ │ │ │ │ │ │ ├── customer-list.component.html │ │ │ │ │ │ │ │ │ │ ├── customer-list.component.scss │ │ │ │ │ │ │ │ │ │ ├── customer-list.component.spec.ts │ │ │ │ │ │ │ │ │ │ └── customer-list.component.ts │ │ │ │ │ │ │ │ │ ├── customers-routing.module.ts │ │ │ │ │ │ │ │ │ ├── customers-ui.module.spec.ts │ │ │ │ │ │ │ │ │ ├── customers-ui.module.ts │ │ │ │ │ │ │ │ │ ├── customers.component.html │ │ │ │ │ │ │ │ │ ├── customers.component.scss │ │ │ │ │ │ │ │ │ ├── customers.component.spec.ts │ │ │ │ │ │ │ │ │ └── customers.component.ts │ │ │ │ │ │ │ │ └── test-setup.ts │ │ │ │ │ │ │ ├── tsconfig.json │ │ │ │ │ │ │ ├── tsconfig.lib.json │ │ │ │ │ │ │ ├── tsconfig.spec.json │ │ │ │ │ │ │ └── tslint.json │ │ │ │ │ │ ├── home/ │ │ │ │ │ │ │ └── ui/ │ │ │ │ │ │ │ ├── .storybook/ │ │ │ │ │ │ │ │ ├── addons.js │ │ │ │ │ │ │ │ ├── config.js │ │ │ │ │ │ │ │ ├── tsconfig.json │ │ │ │ │ │ │ │ └── webpack.config.js │ │ │ │ │ │ │ ├── README.md │ │ │ │ │ │ │ ├── jest.config.js │ │ │ │ │ │ │ ├── src/ │ │ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ │ │ ├── lib/ │ │ │ │ │ │ │ │ │ ├── home-ui.module.spec.ts │ │ │ │ │ │ │ │ │ ├── home-ui.module.ts │ │ │ │ │ │ │ │ │ ├── home.component.html │ │ │ │ │ │ │ │ │ ├── home.component.scss │ │ │ │ │ │ │ │ │ ├── home.component.spec.ts │ │ │ │ │ │ │ │ │ ├── home.component.stories.ts │ │ │ │ │ │ │ │ │ └── home.component.ts │ │ │ │ │ │ │ │ └── test-setup.ts │ │ │ │ │ │ │ ├── tsconfig.json │ │ │ │ │ │ │ ├── tsconfig.lib.json │ │ │ │ │ │ │ ├── tsconfig.spec.json │ │ │ │ │ │ │ └── tslint.json │ │ │ │ │ │ └── shared/ │ │ │ │ │ │ └── components/ │ │ │ │ │ │ ├── .storybook/ │ │ │ │ │ │ │ ├── addons.js │ │ │ │ │ │ │ ├── config.js │ │ │ │ │ │ │ ├── preview-head.html │ │ │ │ │ │ │ ├── tsconfig.json │ │ │ │ │ │ │ └── webpack.config.js │ │ │ │ │ │ ├── README.md │ │ │ │ │ │ ├── jest.config.js │ │ │ │ │ │ ├── src/ │ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ │ ├── lib/ │ │ │ │ │ │ │ │ ├── info-box/ │ │ │ │ │ │ │ │ │ ├── info-box.component.html │ │ │ │ │ │ │ │ │ ├── info-box.component.scss │ │ │ │ │ │ │ │ │ ├── info-box.component.spec.ts │ │ │ │ │ │ │ │ │ ├── info-box.component.stories.ts │ │ │ │ │ │ │ │ │ └── info-box.component.ts │ │ │ │ │ │ │ │ ├── navigation/ │ │ │ │ │ │ │ │ │ ├── navigation.component.html │ │ │ │ │ │ │ │ │ ├── navigation.component.scss │ │ │ │ │ │ │ │ │ ├── navigation.component.spec.ts │ │ │ │ │ │ │ │ │ ├── navigation.component.stories.ts │ │ │ │ │ │ │ │ │ └── navigation.component.ts │ │ │ │ │ │ │ │ ├── shared-components.module.spec.ts │ │ │ │ │ │ │ │ └── shared-components.module.ts │ │ │ │ │ │ │ └── test-setup.ts │ │ │ │ │ │ ├── tsconfig.json │ │ │ │ │ │ ├── tsconfig.lib.json │ │ │ │ │ │ ├── tsconfig.spec.json │ │ │ │ │ │ └── tslint.json │ │ │ │ │ ├── nx.json │ │ │ │ │ ├── package.json │ │ │ │ │ ├── scully.config.js │ │ │ │ │ ├── tools/ │ │ │ │ │ │ ├── schematics/ │ │ │ │ │ │ │ └── .gitkeep │ │ │ │ │ │ └── tsconfig.tools.json │ │ │ │ │ ├── tsconfig.json │ │ │ │ │ └── tslint.json │ │ │ │ ├── preact-app/ │ │ │ │ │ ├── .gitignore │ │ │ │ │ ├── README.md │ │ │ │ │ ├── package.json │ │ │ │ │ └── src/ │ │ │ │ │ ├── .babelrc │ │ │ │ │ ├── components/ │ │ │ │ │ │ ├── app.js │ │ │ │ │ │ ├── header/ │ │ │ │ │ │ │ ├── index.js │ │ │ │ │ │ │ └── style.css │ │ │ │ │ │ └── info.js │ │ │ │ │ ├── index.js │ │ │ │ │ ├── manifest.json │ │ │ │ │ ├── routes/ │ │ │ │ │ │ ├── about/ │ │ │ │ │ │ │ └── index.js │ │ │ │ │ │ ├── home/ │ │ │ │ │ │ │ ├── index.js │ │ │ │ │ │ │ └── style.css │ │ │ │ │ │ └── profile/ │ │ │ │ │ │ ├── index.js │ │ │ │ │ │ └── style.css │ │ │ │ │ ├── style/ │ │ │ │ │ │ └── index.css │ │ │ │ │ └── tests/ │ │ │ │ │ ├── __mocks__/ │ │ │ │ │ │ └── browserMocks.js │ │ │ │ │ └── header.test.js │ │ │ │ ├── react-app/ │ │ │ │ │ ├── .gitignore │ │ │ │ │ ├── README.md │ │ │ │ │ ├── package.json │ │ │ │ │ ├── public/ │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ └── manifest.json │ │ │ │ │ └── src/ │ │ │ │ │ ├── App.css │ │ │ │ │ ├── App.jsx │ │ │ │ │ ├── LazyRoute.jsx │ │ │ │ │ ├── index.css │ │ │ │ │ ├── index.js │ │ │ │ │ ├── intro/ │ │ │ │ │ │ └── Intro.jsx │ │ │ │ │ └── main/ │ │ │ │ │ ├── Main.jsx │ │ │ │ │ ├── kid/ │ │ │ │ │ │ └── Kid.jsx │ │ │ │ │ └── parent/ │ │ │ │ │ └── Parent.jsx │ │ │ │ ├── react-app-ts/ │ │ │ │ │ ├── .gitignore │ │ │ │ │ ├── README.md │ │ │ │ │ ├── package.json │ │ │ │ │ ├── public/ │ │ │ │ │ │ └── index.html │ │ │ │ │ ├── src/ │ │ │ │ │ │ ├── App.css │ │ │ │ │ │ ├── App.tsx │ │ │ │ │ │ ├── LazyRoute.tsx │ │ │ │ │ │ ├── index.css │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ ├── intro/ │ │ │ │ │ │ │ └── Intro.tsx │ │ │ │ │ │ ├── main/ │ │ │ │ │ │ │ ├── Main.tsx │ │ │ │ │ │ │ ├── kid/ │ │ │ │ │ │ │ │ └── Kid.tsx │ │ │ │ │ │ │ └── parent/ │ │ │ │ │ │ │ └── Parent.tsx │ │ │ │ │ │ └── registerServiceWorker.ts │ │ │ │ │ ├── tsconfig.json │ │ │ │ │ ├── tsconfig.test.json │ │ │ │ │ └── tslint.json │ │ │ │ └── unknown/ │ │ │ │ └── package.json │ │ │ ├── parser.spec.ts │ │ │ ├── preact-jsx.spec.ts │ │ │ ├── react-jsx.spec.ts │ │ │ └── react-tsx.spec.ts │ │ ├── tsconfig.json │ │ └── webpack.config.js │ └── guess-webpack/ │ ├── .gitignore │ ├── .npmignore │ ├── README.md │ ├── api/ │ │ └── index.ts │ ├── index.ts │ ├── package.json │ ├── src/ │ │ ├── aot/ │ │ │ ├── aot.tpl │ │ │ └── guess-aot.ts │ │ ├── asset-observer.ts │ │ ├── compress.ts │ │ ├── declarations.ts │ │ ├── ga-provider.ts │ │ ├── guess-webpack.ts │ │ ├── prefetch-aot-plugin.ts │ │ ├── prefetch-plugin.ts │ │ ├── runtime/ │ │ │ ├── guess.ts │ │ │ ├── runtime.tpl │ │ │ └── runtime.ts │ │ └── utils.ts │ ├── test/ │ │ ├── e2e/ │ │ │ ├── delegate.spec.ts │ │ │ ├── next.spec.ts │ │ │ └── prefetch.spec.ts │ │ ├── fixtures/ │ │ │ ├── angular/ │ │ │ │ ├── .editorconfig │ │ │ │ ├── .gitignore │ │ │ │ ├── README.md │ │ │ │ ├── angular.json │ │ │ │ ├── e2e/ │ │ │ │ │ ├── protractor.conf.js │ │ │ │ │ ├── src/ │ │ │ │ │ │ ├── app.e2e-spec.ts │ │ │ │ │ │ └── app.po.ts │ │ │ │ │ └── tsconfig.e2e.json │ │ │ │ ├── package.json │ │ │ │ ├── routes.json │ │ │ │ ├── src/ │ │ │ │ │ ├── app/ │ │ │ │ │ │ ├── app-routing.module.ts │ │ │ │ │ │ ├── app.component.css │ │ │ │ │ │ ├── app.component.html │ │ │ │ │ │ ├── app.component.spec.ts │ │ │ │ │ │ ├── app.component.ts │ │ │ │ │ │ ├── app.module.ts │ │ │ │ │ │ ├── bar/ │ │ │ │ │ │ │ ├── bar.component.ts │ │ │ │ │ │ │ └── bar.module.ts │ │ │ │ │ │ ├── foo/ │ │ │ │ │ │ │ ├── baz/ │ │ │ │ │ │ │ │ ├── baz-routing.module.ts │ │ │ │ │ │ │ │ ├── baz.component.ts │ │ │ │ │ │ │ │ └── baz.module.ts │ │ │ │ │ │ │ ├── foo-routing.module.ts │ │ │ │ │ │ │ ├── foo.component.ts │ │ │ │ │ │ │ └── foo.module.ts │ │ │ │ │ │ └── qux/ │ │ │ │ │ │ ├── qux-routing.module.ts │ │ │ │ │ │ ├── qux.component.ts │ │ │ │ │ │ └── qux.module.ts │ │ │ │ │ ├── assets/ │ │ │ │ │ │ └── .gitkeep │ │ │ │ │ ├── browserslist │ │ │ │ │ ├── environments/ │ │ │ │ │ │ ├── environment.prod.ts │ │ │ │ │ │ └── environment.ts │ │ │ │ │ ├── index.html │ │ │ │ │ ├── karma.conf.js │ │ │ │ │ ├── main.ts │ │ │ │ │ ├── polyfills.ts │ │ │ │ │ ├── styles.css │ │ │ │ │ ├── test.ts │ │ │ │ │ ├── tsconfig.app.json │ │ │ │ │ ├── tsconfig.spec.json │ │ │ │ │ └── tslint.json │ │ │ │ ├── tsconfig.json │ │ │ │ ├── tslint.json │ │ │ │ └── webpack.extra.js │ │ │ ├── delegate/ │ │ │ │ ├── index.html │ │ │ │ ├── index.js │ │ │ │ ├── package.json │ │ │ │ └── webpack.config.js │ │ │ ├── next/ │ │ │ │ ├── components/ │ │ │ │ │ └── layout.js │ │ │ │ ├── next.config.js │ │ │ │ ├── package.json │ │ │ │ └── pages/ │ │ │ │ ├── about.js │ │ │ │ ├── contact.js │ │ │ │ └── index.js │ │ │ └── prefetch/ │ │ │ ├── about.js │ │ │ ├── contact.js │ │ │ ├── home.js │ │ │ ├── index.html │ │ │ ├── index.js │ │ │ ├── package.json │ │ │ └── webpack.config.js │ │ └── unit/ │ │ ├── compress.spec.ts │ │ ├── runtime.spec.ts │ │ └── utils.spec.ts │ ├── tsconfig-api.json │ ├── tsconfig.json │ └── webpack.config.js ├── renovate.json ├── tsconfig.json └── tslint.json
SYMBOL INDEX (250 symbols across 107 files)
FILE: experiments/guess-static-sites/predictiveFetching.js
constant ENDPOINT (line 1) | const ENDPOINT = 'http://YOUR_SERVER_ENDPOINT/'
FILE: experiments/guess-static-sites/test/clientTests.js
constant SERVER_URL (line 13) | const SERVER_URL = 'http://localhost:3000/'
constant PAGE_URL (line 14) | const PAGE_URL = 'http://localhost:8080/test.html'
FILE: experiments/guess-static-sites/test/fixtures/server.js
constant PORT (line 8) | const PORT = 8080
FILE: infra/test.ts
function main (line 21) | async function main() {
FILE: packages/common/interfaces.ts
type Neighbors (line 1) | interface Neighbors {
type Graph (line 5) | interface Graph {
type Module (line 9) | interface Module {
type RoutingModule (line 14) | interface RoutingModule {
type Connection (line 22) | interface Connection {
type Period (line 28) | interface Period {
type ProjectType (line 33) | enum ProjectType {
type ProjectLayout (line 41) | interface ProjectLayout {
type ProjectMetadata (line 47) | interface ProjectMetadata {
FILE: packages/common/logger.ts
type LogLevel (line 1) | enum LogLevel {
class Logger (line 9) | class Logger {
method constructor (line 10) | constructor(private level = LogLevel.INFO) {}
method setLevel (line 12) | setLevel(newLevel: LogLevel) {
method debug (line 16) | debug(...msg: any[]) {
method info (line 20) | info(...msg: any[]) {
method warn (line 24) | warn(...msg: any[]) {
method error (line 28) | error(...msg: any[]) {
method print (line 32) | private print(level: LogLevel, label: string, msg: any[]) {
method prettify (line 54) | private prettify(label: string) {
FILE: packages/guess-ga/src/client.ts
type PageConfig (line 3) | interface PageConfig {
type AnalyticsResult (line 8) | interface AnalyticsResult {
function requestBuilder (line 17) | function requestBuilder(jwtClient: any, viewId: string, pageConfig: Page...
function fetchReport (line 39) | async function fetchReport(
type GaResult (line 70) | type GaResult = any;
type ClientResult (line 72) | interface ClientResult {
function getClient (line 77) | function getClient(jwtClient: any, pageSize: number, viewId: string, per...
FILE: packages/guess-ga/src/ga.ts
type FetchConfig (line 9) | interface FetchConfig {
function fetch (line 18) | async function fetch(config: FetchConfig) {
FILE: packages/guess-parser/src/angular/modules.ts
type RoutesDeclaration (line 8) | interface RoutesDeclaration {
type Registry (line 13) | interface Registry {
FILE: packages/guess-parser/src/angular/route-parser.ts
type Options (line 16) | interface Options {
FILE: packages/guess-parser/src/angular/routes.ts
type Route (line 117) | interface Route {
type LazyRoute (line 123) | interface LazyRoute extends Route {
FILE: packages/guess-parser/test/fixtures/angular/library/library.module.ts
class LibraryComponent (line 4) | @Component({
class LibraryModule (line 20) | class LibraryModule {}
FILE: packages/guess-parser/test/fixtures/angular/library/nested/library.module.ts
class LibraryComponent (line 4) | @Component({
class LibraryModule (line 20) | class LibraryModule {}
FILE: packages/guess-parser/test/fixtures/angular/src/app/about/about-routing.module.ts
class AboutRoutingModule (line 10) | class AboutRoutingModule {}
FILE: packages/guess-parser/test/fixtures/angular/src/app/about/about.module.ts
class AboutModule (line 13) | class AboutModule { }
FILE: packages/guess-parser/test/fixtures/angular/src/app/app-routing.module.ts
class AppRoutingModule (line 68) | class AppRoutingModule {}
FILE: packages/guess-parser/test/fixtures/angular/src/app/app.component.ts
class AppComponent (line 8) | class AppComponent {
FILE: packages/guess-parser/test/fixtures/angular/src/app/app.module.ts
class AppModule (line 13) | class AppModule {}
FILE: packages/guess-parser/test/fixtures/angular/src/app/bar-simple.component.ts
class BarSimpleComponent (line 7) | class BarSimpleComponent {}
FILE: packages/guess-parser/test/fixtures/angular/src/app/bar/bar.module.ts
class BarModule (line 14) | class BarModule {}
FILE: packages/guess-parser/test/fixtures/angular/src/app/bar/baz/baz.module.ts
class BazComponent (line 9) | class BazComponent {}
class BazModule (line 24) | class BazModule {}
FILE: packages/guess-parser/test/fixtures/angular/src/app/foo/baz/baz-routing.module.ts
class BazRoutingModule (line 20) | class BazRoutingModule {}
FILE: packages/guess-parser/test/fixtures/angular/src/app/foo/baz/baz.component.ts
class BazComponent (line 7) | class BazComponent {}
FILE: packages/guess-parser/test/fixtures/angular/src/app/foo/baz/baz.module.ts
class FooModule (line 10) | class FooModule {}
FILE: packages/guess-parser/test/fixtures/angular/src/app/foo/foo-routing.module.ts
class FooRoutingModule (line 44) | class FooRoutingModule {}
FILE: packages/guess-parser/test/fixtures/angular/src/app/foo/foo.component.ts
class FooComponent (line 7) | class FooComponent {}
FILE: packages/guess-parser/test/fixtures/angular/src/app/foo/foo.module.ts
class FooModule (line 10) | class FooModule {}
FILE: packages/guess-parser/test/fixtures/angular/src/app/lazy/lazy-routing.module.ts
class LazyRoutingModule (line 12) | class LazyRoutingModule {}
FILE: packages/guess-parser/test/fixtures/angular/src/app/lazy/lazy.component.ts
class LazyComponent (line 6) | class LazyComponent { }
FILE: packages/guess-parser/test/fixtures/angular/src/app/lazy/lazy.module.ts
class LazyModule (line 16) | class LazyModule { }
FILE: packages/guess-parser/test/fixtures/angular/src/app/qux/qux.module.ts
class QuxComponent (line 4) | @Component({
class QuxModule (line 21) | class QuxModule {}
FILE: packages/guess-parser/test/fixtures/angular/src/app/wrapper/wrapper.module.ts
class WrapperModule (line 7) | class WrapperModule {}
FILE: packages/guess-parser/test/fixtures/ng8/e2e/protractor.conf.js
method onPrepare (line 26) | onPrepare() {
FILE: packages/guess-parser/test/fixtures/ng8/e2e/src/app.po.ts
class AppPage (line 3) | class AppPage {
method navigateTo (line 4) | navigateTo() {
method getTitleText (line 8) | getTitleText() {
FILE: packages/guess-parser/test/fixtures/ng8/src/app/app-routing.module.ts
class AppRoutingModule (line 10) | class AppRoutingModule { }
FILE: packages/guess-parser/test/fixtures/ng8/src/app/app.component.ts
class AppComponent (line 8) | class AppComponent {
FILE: packages/guess-parser/test/fixtures/ng8/src/app/app.module.ts
class AppModule (line 18) | class AppModule { }
FILE: packages/guess-parser/test/fixtures/nx/apps/feat-home-e2e/src/support/commands.ts
type Chainable (line 14) | interface Chainable<Subject> {
FILE: packages/guess-parser/test/fixtures/nx/apps/ng-cli-app-e2e/src/support/commands.ts
type Chainable (line 31) | interface Chainable<Subject> {
FILE: packages/guess-parser/test/fixtures/nx/apps/ng-cli-app/src/app/app.component.ts
class AppComponent (line 11) | class AppComponent implements OnInit {
method constructor (line 14) | constructor(
method ngOnInit (line 19) | ngOnInit(): void {
method logout (line 23) | public logout() {
FILE: packages/guess-parser/test/fixtures/nx/apps/ng-cli-app/src/app/app.module.ts
class AppModule (line 43) | class AppModule {}
FILE: packages/guess-parser/test/fixtures/nx/apps/shared-components-e2e/src/support/commands.ts
type Chainable (line 12) | interface Chainable<Subject> {
FILE: packages/guess-parser/test/fixtures/nx/libs/auth/src/lib/auth-routing.module.ts
constant AUTH_ROUTES (line 5) | const AUTH_ROUTES: Routes = [
FILE: packages/guess-parser/test/fixtures/nx/libs/auth/src/lib/auth.guard.ts
class AuthGuard (line 16) | class AuthGuard implements CanActivate {
method constructor (line 17) | constructor(private authService: AuthService, private router: Router) {}
method canActivate (line 19) | canActivate(
FILE: packages/guess-parser/test/fixtures/nx/libs/auth/src/lib/auth.module.ts
class AuthModule (line 24) | class AuthModule {}
FILE: packages/guess-parser/test/fixtures/nx/libs/auth/src/lib/auth.service.ts
class AuthService (line 9) | class AuthService {
method constructor (line 16) | constructor(private router: Router) {}
method login (line 18) | public login(username: string, password: string): Observable<User> {
method logout (line 31) | public logout() {
type User (line 37) | interface User {
FILE: packages/guess-parser/test/fixtures/nx/libs/auth/src/lib/login/login.component.ts
class LoginComponent (line 11) | class LoginComponent implements OnInit {
method constructor (line 16) | constructor(fb: FormBuilder, private authService: AuthService) {
method ngOnInit (line 23) | ngOnInit() {}
method onSubmit (line 25) | public onSubmit() {
FILE: packages/guess-parser/test/fixtures/nx/libs/customers/data/src/lib/customer.model.ts
type Customer (line 1) | interface Customer {
FILE: packages/guess-parser/test/fixtures/nx/libs/customers/data/src/lib/customer.service.ts
class CustomerService (line 10) | class CustomerService {
method constructor (line 11) | constructor(private httpClient: HttpClient) {}
method getCustomers (line 13) | public getCustomers(): Observable<Customer[]> {
method getCustomerOfTheDay (line 19) | public getCustomerOfTheDay(): Observable<Customer> {
FILE: packages/guess-parser/test/fixtures/nx/libs/customers/ui/src/lib/customer-list/customer-list-datasource.ts
class CustomerListDataSource (line 13) | class CustomerListDataSource extends DataSource<Customer> {
method constructor (line 21) | constructor(private customerService: CustomerService) {
method connect (line 30) | connect(): Observable<Customer[]> {
method disconnect (line 55) | disconnect() {
method getPagedData (line 63) | private getPagedData(data: Customer[]) {
method getSortedData (line 72) | private getSortedData(data: Customer[]) {
function compare (line 92) | function compare(a, b, isAsc) {
FILE: packages/guess-parser/test/fixtures/nx/libs/customers/ui/src/lib/customer-list/customer-list.component.ts
class CustomerListComponent (line 14) | class CustomerListComponent implements AfterViewInit, OnInit {
method constructor (line 31) | constructor(private customerService: CustomerService) {}
method ngOnInit (line 33) | ngOnInit() {
method ngAfterViewInit (line 37) | ngAfterViewInit() {
FILE: packages/guess-parser/test/fixtures/nx/libs/customers/ui/src/lib/customers-routing.module.ts
class CustomersRoutingModule (line 22) | class CustomersRoutingModule {}
FILE: packages/guess-parser/test/fixtures/nx/libs/customers/ui/src/lib/customers-ui.module.ts
class CustomersUiModule (line 22) | class CustomersUiModule {}
FILE: packages/guess-parser/test/fixtures/nx/libs/customers/ui/src/lib/customers.component.ts
class CustomersComponent (line 8) | class CustomersComponent implements OnInit {
method constructor (line 9) | constructor() {}
method ngOnInit (line 11) | ngOnInit() {}
FILE: packages/guess-parser/test/fixtures/nx/libs/home/ui/src/lib/home-ui.module.ts
class HomeUiModule (line 14) | class HomeUiModule {}
FILE: packages/guess-parser/test/fixtures/nx/libs/home/ui/src/lib/home.component.ts
class HomeComponent (line 11) | class HomeComponent implements OnInit {
method constructor (line 14) | constructor(private customerService: CustomerService) {}
method ngOnInit (line 16) | ngOnInit() {
FILE: packages/guess-parser/test/fixtures/nx/libs/shared/components/src/lib/info-box/info-box.component.ts
class InfoBoxComponent (line 8) | class InfoBoxComponent implements OnInit {
method constructor (line 13) | constructor() {}
method ngOnInit (line 15) | ngOnInit() {}
FILE: packages/guess-parser/test/fixtures/nx/libs/shared/components/src/lib/navigation/navigation.component.ts
class NavigationComponent (line 11) | class NavigationComponent {
method constructor (line 19) | constructor(private breakpointObserver: BreakpointObserver) {}
FILE: packages/guess-parser/test/fixtures/nx/libs/shared/components/src/lib/shared-components.module.ts
class SharedComponentsModule (line 29) | class SharedComponentsModule {}
FILE: packages/guess-parser/test/fixtures/preact-app/src/components/app.js
class App (line 16) | class App extends Component {
method render (line 25) | render() {
FILE: packages/guess-parser/test/fixtures/preact-app/src/components/header/index.js
class Header (line 5) | class Header extends Component {
method render (line 6) | render() {
FILE: packages/guess-parser/test/fixtures/preact-app/src/components/info.js
class Info (line 3) | class Info extends Component {
method render (line 4) | render() {
FILE: packages/guess-parser/test/fixtures/preact-app/src/routes/about/index.js
class About (line 3) | class About extends Component {
method render (line 4) | render() {
FILE: packages/guess-parser/test/fixtures/preact-app/src/routes/home/index.js
class Home (line 4) | class Home extends Component {
method render (line 5) | render() {
FILE: packages/guess-parser/test/fixtures/preact-app/src/routes/profile/index.js
class Profile (line 4) | class Profile extends Component {
method componentDidMount (line 11) | componentDidMount() {
method componentWillUnmount (line 17) | componentWillUnmount() {
method render (line 31) | render({ user }, { time, count }) {
FILE: packages/guess-parser/test/fixtures/react-app-ts/src/App.tsx
class App (line 10) | class App extends React.Component {
method render (line 11) | public render() {
FILE: packages/guess-parser/test/fixtures/react-app-ts/src/intro/Intro.tsx
class Intro (line 4) | class Intro extends React.Component {
method render (line 5) | public render() {
FILE: packages/guess-parser/test/fixtures/react-app-ts/src/main/Main.tsx
class Main (line 7) | class Main extends React.Component {
method render (line 8) | public render() {
FILE: packages/guess-parser/test/fixtures/react-app-ts/src/main/kid/Kid.tsx
class Kid (line 3) | class Kid extends React.Component {
method render (line 4) | public render() {
FILE: packages/guess-parser/test/fixtures/react-app-ts/src/main/parent/Parent.tsx
class Parent (line 3) | class Parent extends React.Component {
method render (line 4) | public render() {
FILE: packages/guess-parser/test/fixtures/react-app-ts/src/registerServiceWorker.ts
function register (line 22) | function register() {
function registerValidSW (line 59) | function registerValidSW(swUrl: string) {
function checkValidServiceWorker (line 90) | function checkValidServiceWorker(swUrl: string) {
function unregister (line 117) | function unregister() {
FILE: packages/guess-parser/test/fixtures/react-app/src/App.jsx
class App (line 10) | class App extends React.Component {
method render (line 11) | render() {
FILE: packages/guess-parser/test/fixtures/react-app/src/intro/Intro.jsx
class Intro (line 4) | class Intro extends React.Component {
method render (line 5) | render() {
FILE: packages/guess-parser/test/fixtures/react-app/src/main/Main.jsx
class Main (line 7) | class Main extends React.Component {
method render (line 8) | render() {
FILE: packages/guess-parser/test/fixtures/react-app/src/main/kid/Kid.jsx
class Kid (line 3) | class Kid extends React.Component {
method render (line 4) | render() {
FILE: packages/guess-parser/test/fixtures/react-app/src/main/parent/Parent.jsx
class Parent (line 3) | class Parent extends React.Component {
method render (line 4) | render() {
FILE: packages/guess-webpack/src/aot/guess-aot.ts
type ConnectionEffectiveType (line 3) | type ConnectionEffectiveType = '4g' | '3g' | '2g' | 'slow-2g';
FILE: packages/guess-webpack/src/asset-observer.ts
type Asset (line 1) | interface Asset {
type AssetCallback (line 6) | type AssetCallback = (asset: Asset) => void;
class AssetObserver (line 8) | class AssetObserver {
method onAsset (line 12) | onAsset(cb: AssetCallback) {
method addAsset (line 16) | addAsset(asset: Asset) {
FILE: packages/guess-webpack/src/declarations.ts
type Mode (line 3) | enum Mode {
type RouteProvider (line 12) | type RouteProvider = () => Promise<RoutingModule[]>;
type Cluster (line 14) | type Cluster = string[];
type Clusters (line 15) | type Clusters = Cluster[];
type ClusteringAlgorithm (line 17) | type ClusteringAlgorithm = (graph: Graph, modules: Module[], totalCluste...
type Module (line 19) | interface Module {
type CompressedPrefetchGraph (line 24) | type CompressedPrefetchGraph = number[][][];
type CompressedGraphMap (line 26) | interface CompressedGraphMap {
type PrefetchConfig (line 31) | interface PrefetchConfig {
type PrefetchPluginConfig (line 38) | interface PrefetchPluginConfig {
type BundleEntryNeighbor (line 47) | interface BundleEntryNeighbor {
type BundleEntryGraph (line 53) | interface BundleEntryGraph {
type PrefetchNeighbor (line 57) | interface PrefetchNeighbor {
type PrefetchGraph (line 63) | interface PrefetchGraph {
type PrefetchAotNeighbor (line 67) | interface PrefetchAotNeighbor {
type FileChunkMap (line 72) | interface FileChunkMap {
type PrefetchAotGraph (line 76) | interface PrefetchAotGraph {
type PrefetchAotPluginConfig (line 80) | interface PrefetchAotPluginConfig {
FILE: packages/guess-webpack/src/ga-provider.ts
type Config (line 15) | interface Config {
FILE: packages/guess-webpack/src/guess-webpack.ts
type RuntimeConfig (line 8) | interface RuntimeConfig {
type GuessPluginConfig (line 17) | interface GuessPluginConfig {
class GuessPlugin (line 44) | class GuessPlugin {
method constructor (line 45) | constructor(private _config: GuessPluginConfig) {
method apply (line 59) | apply(compiler: any) {
method _execute (line 81) | private _execute(compiler: any, compilation: any, assetObserver: Asset...
method _getReport (line 101) | private _getReport(routes: RoutingModule[]): Promise<Graph> {
method _executePrefetchPlugin (line 115) | private _executePrefetchPlugin(
FILE: packages/guess-webpack/src/prefetch-aot-plugin.ts
class PrefetchAotPlugin (line 84) | class PrefetchAotPlugin {
method constructor (line 86) | constructor(private _config: PrefetchAotPluginConfig) {
method execute (line 95) | execute(
FILE: packages/guess-webpack/src/prefetch-plugin.ts
class PrefetchPlugin (line 21) | class PrefetchPlugin {
method constructor (line 24) | constructor(private _config: PrefetchPluginConfig) {
method execute (line 30) | execute(compilation: any, callback: any) {
FILE: packages/guess-webpack/src/runtime/guess.ts
type GuessFn (line 3) | type GuessFn = (params?: Partial<GuessFnParams>) => Predictions;
type GuessFnParams (line 5) | interface GuessFnParams {
type Probability (line 11) | type Probability = number;
type ConnectionEffectiveType (line 12) | type ConnectionEffectiveType = '4g' | '3g' | '2g' | 'slow-2g';
type ConnectionEffectiveTypeThresholds (line 14) | interface ConnectionEffectiveTypeThresholds {
type Predictions (line 21) | interface Predictions {
type Navigation (line 25) | interface Navigation {
type Navigations (line 30) | interface Navigations {
class GraphNode (line 57) | class GraphNode {
method constructor (line 58) | constructor(private _node: number[], private _map: CompressedGraphMap) {}
method probability (line 60) | get probability() {
method route (line 64) | get route() {
method chunk (line 68) | get chunk() {
class Graph (line 73) | class Graph {
method constructor (line 74) | constructor(private _graph: CompressedPrefetchGraph, private _map: Com...
method findMatch (line 76) | findMatch(route: string): GraphNode[] {
FILE: packages/guess-webpack/src/utils.ts
type Compilation (line 69) | interface Compilation {
type JSReason (line 73) | interface JSReason {
type JSModule (line 78) | interface JSModule {
type JSOrigin (line 85) | interface JSOrigin {
type JSChunk (line 91) | interface JSChunk {
type JSCompilation (line 99) | interface JSCompilation {
type ChunkGraph (line 104) | interface ChunkGraph {
function getModulePath (line 159) | function getModulePath(moduleName: string): string | null {
FILE: packages/guess-webpack/test/fixtures/angular/e2e/protractor.conf.js
method onPrepare (line 22) | onPrepare() {
FILE: packages/guess-webpack/test/fixtures/angular/e2e/src/app.po.ts
class AppPage (line 3) | class AppPage {
method navigateTo (line 4) | navigateTo() {
method getTitleText (line 8) | getTitleText() {
FILE: packages/guess-webpack/test/fixtures/angular/src/app/app-routing.module.ts
class AppRoutingModule (line 29) | class AppRoutingModule {}
FILE: packages/guess-webpack/test/fixtures/angular/src/app/app.component.ts
class AppComponent (line 8) | class AppComponent {
FILE: packages/guess-webpack/test/fixtures/angular/src/app/app.module.ts
class AppModule (line 14) | class AppModule {}
FILE: packages/guess-webpack/test/fixtures/angular/src/app/bar/bar.component.ts
class BarComponent (line 7) | class BarComponent {}
FILE: packages/guess-webpack/test/fixtures/angular/src/app/bar/bar.module.ts
class BarModule (line 8) | class BarModule {}
FILE: packages/guess-webpack/test/fixtures/angular/src/app/foo/baz/baz-routing.module.ts
class BazRoutingModule (line 20) | class BazRoutingModule {}
FILE: packages/guess-webpack/test/fixtures/angular/src/app/foo/baz/baz.component.ts
class BazComponent (line 7) | class BazComponent {}
FILE: packages/guess-webpack/test/fixtures/angular/src/app/foo/baz/baz.module.ts
class BazModule (line 10) | class BazModule {}
FILE: packages/guess-webpack/test/fixtures/angular/src/app/foo/foo-routing.module.ts
class FooRoutingModule (line 25) | class FooRoutingModule {}
FILE: packages/guess-webpack/test/fixtures/angular/src/app/foo/foo.component.ts
class FooComponent (line 7) | class FooComponent {}
FILE: packages/guess-webpack/test/fixtures/angular/src/app/foo/foo.module.ts
class FooModule (line 10) | class FooModule {}
FILE: packages/guess-webpack/test/fixtures/angular/src/app/qux/qux-routing.module.ts
class QuxRoutingModule (line 20) | class QuxRoutingModule {}
FILE: packages/guess-webpack/test/fixtures/angular/src/app/qux/qux.component.ts
class QuxComponent (line 7) | class QuxComponent {}
FILE: packages/guess-webpack/test/fixtures/angular/src/app/qux/qux.module.ts
class QuxModule (line 10) | class QuxModule {}
FILE: packages/guess-webpack/test/fixtures/angular/webpack.extra.js
method reportProvider (line 8) | reportProvider() {
method routeProvider (line 14) | routeProvider() {
FILE: packages/guess-webpack/test/fixtures/delegate/webpack.config.js
method reportProvider (line 22) | reportProvider() {
FILE: packages/guess-webpack/test/fixtures/next/next.config.js
method reportProvider (line 9) | reportProvider() {
FILE: packages/guess-webpack/test/fixtures/next/pages/index.js
class Index (line 4) | class Index extends React.Component {
method render (line 5) | render() {
FILE: packages/guess-webpack/test/fixtures/prefetch/webpack.config.js
method reportProvider (line 50) | reportProvider() {
FILE: packages/guess-webpack/test/unit/utils.spec.ts
method getStats (line 27) | getStats() {
Condensed preview — 452 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (715K chars).
[
{
"path": ".gitignore",
"chars": 109,
"preview": ".DS_Store\n.idea\n.vscode\nbuild\nnode_modules\nnpm-debug.log\ntemp\ntmp-*\n.esm-cache/\ndist\nlerna-debug.log\n*.d.ts\n\n"
},
{
"path": ".travis.yml",
"chars": 212,
"preview": "language: node_js\nnode_js: stable\nos: linux\nsudo: required\n\ninstall:\n - npm run bootstrap\nscript:\n - npm run build\n -"
},
{
"path": "CONTRIBUTING.md",
"chars": 2658,
"preview": "# Contributing to Guess.js\n\nPlease contribute to this repository if any of the following is true:\n\n- You are interested "
},
{
"path": "DEVELOPING.md",
"chars": 794,
"preview": "# Developer's Guide\n\nThis document explains how to build, test, and publish the packages from the monorepo.\n\n## Installa"
},
{
"path": "LICENSE",
"chars": 1116,
"preview": "MIT License\n\nCopyright (c) 2018 Minko Gechev <mgechev@gmail.com> and the Guess \ncontributors\n\nPermission is hereby grant"
},
{
"path": "README.md",
"chars": 31324,
"preview": "[](https://travis-ci.com/guess-js/guess)\n\n# Guess"
},
{
"path": "experiments/guess-static-sites/.gitignore",
"chars": 38,
"preview": "*.env\n*.pem\n*.p12\nnode_modules/\n*.swp\n"
},
{
"path": "experiments/guess-static-sites/README.md",
"chars": 6511,
"preview": "# guess-static-sites\n\nGuess.js for non-Webpack sites.\n\n*Automatic, dynamic, intelligent prefetching for faster page load"
},
{
"path": "experiments/guess-static-sites/config.js",
"chars": 1006,
"preview": "const env = process.env.NODE_ENV\n\nrequire('dotenv').config()\n\nconst dev = {\n auth: {\n keyFileName: 'key.pem',\n vi"
},
{
"path": "experiments/guess-static-sites/generatePredictions.js",
"chars": 952,
"preview": "/*\nThis scripts retrieves recent reporting data from Google Analytics\nand uses this to determine the \"Most Likely Next P"
},
{
"path": "experiments/guess-static-sites/package.json",
"chars": 974,
"preview": "{\n \"name\": \"guess-static-sites\",\n \"version\": \"1.0.0\",\n \"description\": \"\",\n \"main\": \"server.js\",\n \"scripts\": {\n \""
},
{
"path": "experiments/guess-static-sites/predictiveFetching.js",
"chars": 1600,
"preview": "const ENDPOINT = 'http://YOUR_SERVER_ENDPOINT/'\n\nconst setCookie = function (obj) {\n const cookieStr = obj.name + '=' +"
},
{
"path": "experiments/guess-static-sites/server.js",
"chars": 2332,
"preview": "const express = require('express')\nconst bodyParser = require('body-parser')\nconst cors = require('cors')\nconst mongoose"
},
{
"path": "experiments/guess-static-sites/src/models/pageView.js",
"chars": 283,
"preview": "const mongoose = require('mongoose')\n\nconst pageViewSchema = mongoose.Schema({\n pagePath: String,\n clientInfo: Object,"
},
{
"path": "experiments/guess-static-sites/src/models/prediction.js",
"chars": 251,
"preview": "const mongoose = require('mongoose')\n\nconst predictionSchema = mongoose.Schema({\n pagePath: String,\n nextPagePath: Str"
},
{
"path": "experiments/guess-static-sites/src/parser.js",
"chars": 3329,
"preview": "const mongoose = require('mongoose')\nconst Prediction = require('./models/prediction')\nconst config = require('../config"
},
{
"path": "experiments/guess-static-sites/src/queryParams.js",
"chars": 658,
"preview": "const config = require('../config')\n\nconst queryParams = {\n resource: {\n reportRequests: [\n {\n viewId: c"
},
{
"path": "experiments/guess-static-sites/test/clientTests.js",
"chars": 6564,
"preview": "// To run tests: $ npm run script-tests\n// Server responses are stubbed, but test server needs to be running\n// ($ NODE_"
},
{
"path": "experiments/guess-static-sites/test/fixtures/gaResponse.json",
"chars": 2846,
"preview": "{\"testData\": [{\n\t\"columnHeader\": {\n\t\t\"dimensions\": [\"ga:previousPagePath\", \"ga:pagePath\"],\n\t\t\"metricHeader\": {\n\t\t\t\"metri"
},
{
"path": "experiments/guess-static-sites/test/fixtures/server.js",
"chars": 720,
"preview": "// Simple static server for serving a page for testing front-end script\nconst express = require('express')\nconst cors = "
},
{
"path": "experiments/guess-static-sites/test/fixtures/test.html",
"chars": 208,
"preview": "<!DOCTYPE html>\n<html>\n<head>\n <meta charset=\"utf-8\">\n <title>Next Page Predictor</title>\n</head>\n<body>\nPage for test"
},
{
"path": "experiments/guess-static-sites/test/gaClientTests.js",
"chars": 1266,
"preview": "// To run tests: $ npm run generate-predictions-tests\n// mongod should be running\n\nconst chai = require('chai')\nconst ex"
},
{
"path": "experiments/guess-static-sites/test/serverTests.js",
"chars": 6319,
"preview": "// $ npm run server-tests\nconst request = require('supertest')\nconst express = require('express')\nconst chai = require('"
},
{
"path": "infra/e2e.ts",
"chars": 1930,
"preview": "import { execSync } from 'child_process';\nimport { readFileSync } from 'fs';\n\nconst enterTest = 'cd packages/guess-webpa"
},
{
"path": "infra/install.ts",
"chars": 254,
"preview": "import { join } from 'path';\nimport { execSync } from 'child_process';\n\nconst PackagesDir = join(process.cwd(), 'package"
},
{
"path": "infra/pretest.ts",
"chars": 377,
"preview": "import { readdirSync } from 'fs';\nimport { join } from 'path';\nimport { execSync } from 'child_process';\n\nconst cwd = pr"
},
{
"path": "infra/test.ts",
"chars": 895,
"preview": "import { join } from 'path';\nimport { spawn } from 'child_process';\nimport chalk from 'chalk';\n\nconst StaticServer = req"
},
{
"path": "jest-puppeteer.config.js",
"chars": 113,
"preview": "module.exports = {\n launch: {\n args: ['--no-sandbox', '--disable-setuid-sandbox'],\n headless: true\n }\n};\n"
},
{
"path": "jest.config.js",
"chars": 506,
"preview": "module.exports = {\n transform: {\n '^.+\\\\.tsx?$': 'ts-jest'\n },\n testRegex: '(/test/.*|(\\\\.|/)(test|spec))\\\\.(jsx?|"
},
{
"path": "lerna.json",
"chars": 85,
"preview": "{\n \"lerna\": \"2.11.0\",\n \"packages\": [\n \"packages/*\"\n ],\n \"version\": \"0.4.22\"\n}\n"
},
{
"path": "package.json",
"chars": 1534,
"preview": "{\n \"name\": \"guess\",\n \"version\": \"0.0.0\",\n \"description\": \"Smart bundling\",\n \"main\": \"index.js\",\n \"scripts\": {\n \""
},
{
"path": "packages/common/interfaces.ts",
"chars": 903,
"preview": "export interface Neighbors {\n [key: string]: number;\n}\n\nexport interface Graph {\n [key: string]: Neighbors;\n}\n\nexport "
},
{
"path": "packages/common/logger.ts",
"chars": 1204,
"preview": "export enum LogLevel {\n DEBUG,\n INFO,\n WARN,\n ERROR,\n OFF\n}\n\nexport class Logger {\n constructor(private level = Lo"
},
{
"path": "packages/guess-ga/.npmignore",
"chars": 63,
"preview": "node_modules\nsrc\ntest\nindex.ts\ntsconfig.json\nwebpack.config.js\n"
},
{
"path": "packages/guess-ga/README.md",
"chars": 3762,
"preview": "# GA\n\nFetches data from Google analytics.\n\n## Setup\n\n### Create Your Credentials\n\n#### i) Create a Service Account\n\n1. G"
},
{
"path": "packages/guess-ga/index.ts",
"chars": 26,
"preview": "export * from './src/ga';\n"
},
{
"path": "packages/guess-ga/package.json",
"chars": 1083,
"preview": "{\n \"name\": \"guess-ga\",\n \"version\": \"0.4.20\",\n \"description\": \"Fetch structured data from Google Analytics\",\n \"main\":"
},
{
"path": "packages/guess-ga/src/client.ts",
"chars": 2714,
"preview": "import { Period } from '../../common/interfaces';\n\ninterface PageConfig {\n pageToken: number | undefined;\n pageSize: n"
},
{
"path": "packages/guess-ga/src/ga.ts",
"chars": 948,
"preview": "import { getClient } from './client';\nimport { normalize } from './normalize';\nimport { Graph, Period } from '../../comm"
},
{
"path": "packages/guess-ga/src/normalize.ts",
"chars": 1248,
"preview": "import { Connection } from '../../common/interfaces';\n\nexport const matchRoute = (route: string, declaration: string): b"
},
{
"path": "packages/guess-ga/test/normalize.spec.ts",
"chars": 1464,
"preview": "import { matchRoute, normalize } from '../src/normalize';\n\nconst GAData = {\n rows: [\n {\n dimensions: ['/a', '/b"
},
{
"path": "packages/guess-ga/tsconfig.json",
"chars": 300,
"preview": "{\n \"compilerOptions\": {\n \"target\": \"es5\",\n \"module\": \"commonjs\",\n \"sourceMap\": true,\n \"declaration\": true,\n"
},
{
"path": "packages/guess-ga/webpack.config.js",
"chars": 485,
"preview": "module.exports = {\n mode: 'production',\n entry: './index.ts',\n target: 'node',\n output: {\n filename: './guess-ga/"
},
{
"path": "packages/guess-parser/.npmignore",
"chars": 63,
"preview": "node_modules\nsrc\ntest\nindex.ts\ntsconfig.json\nwebpack.config.js\n"
},
{
"path": "packages/guess-parser/README.md",
"chars": 2764,
"preview": "# guess-parser\n\nThis module is used for route extraction by the `GuessPlugin`. The module exports several functions:\n\n##"
},
{
"path": "packages/guess-parser/index.ts",
"chars": 211,
"preview": "export { parseRoutes } from './src/parser';\nexport { detect } from './src/detector';\nexport { parseRoutes as parseAngula"
},
{
"path": "packages/guess-parser/package.json",
"chars": 1097,
"preview": "{\n \"name\": \"guess-parser\",\n \"version\": \"0.4.22\",\n \"description\": \"Finds the route declarations in your application.\","
},
{
"path": "packages/guess-parser/src/angular/index.ts",
"chars": 32,
"preview": "export * from './route-parser';\n"
},
{
"path": "packages/guess-parser/src/angular/modules.ts",
"chars": 9104,
"preview": "import * as ts from 'typescript';\nimport { resolve, join, dirname, sep } from 'path';\nimport { existsSync } from 'fs';\ni"
},
{
"path": "packages/guess-parser/src/angular/route-parser.ts",
"chars": 3786,
"preview": "import * as ts from 'typescript';\nimport { RoutingModule } from '../../../common/interfaces';\nimport { existsSync, readF"
},
{
"path": "packages/guess-parser/src/angular/routes.ts",
"chars": 4768,
"preview": "import * as ts from 'typescript';\nimport { evaluate } from 'ts-evaluator';\nimport { getModuleEntryPoint, getModulePathFr"
},
{
"path": "packages/guess-parser/src/detector/detect.ts",
"chars": 2097,
"preview": "import { readFileSync, existsSync } from 'fs';\nimport { join } from 'path';\nimport { ProjectType, ProjectMetadata } from"
},
{
"path": "packages/guess-parser/src/detector/index.ts",
"chars": 26,
"preview": "export * from './detect';\n"
},
{
"path": "packages/guess-parser/src/language-service.ts",
"chars": 957,
"preview": "import { existsSync, readFileSync } from 'fs';\nimport * as ts from 'typescript';\n\nexport const getLanguageService = (roo"
},
{
"path": "packages/guess-parser/src/parser.ts",
"chars": 1555,
"preview": "import { parseRoutes as ngParseRoutes } from './angular';\nimport { parseReactTSXRoutes, parseReactJSXRoutes } from './re"
},
{
"path": "packages/guess-parser/src/preact/index.ts",
"chars": 4138,
"preview": "import * as ts from 'typescript';\nimport * as path from 'path';\nimport { RoutingModule } from '../../../common/interface"
},
{
"path": "packages/guess-parser/src/react/base.ts",
"chars": 3324,
"preview": "import * as ts from 'typescript';\nimport * as path from 'path';\nimport { RoutingModule } from '../../../common/interface"
},
{
"path": "packages/guess-parser/src/react/index.ts",
"chars": 175,
"preview": "export { parseReactRoutes } from './base';\nexport { parseRoutes as parseReactTSXRoutes } from './react-tsx';\nexport { pa"
},
{
"path": "packages/guess-parser/src/react/react-jsx.ts",
"chars": 335,
"preview": "import { parseReactRoutes } from '.';\nimport { JsxEmit } from 'typescript';\nimport { RoutingModule } from '../../../comm"
},
{
"path": "packages/guess-parser/src/react/react-tsx.ts",
"chars": 1205,
"preview": "import { readFileSync, lstatSync, existsSync } from 'fs';\nimport * as ts from 'typescript';\nimport { join, dirname, reso"
},
{
"path": "packages/guess-parser/src/utils.ts",
"chars": 528,
"preview": "import { statSync, readdirSync } from 'fs';\nimport { join } from 'path';\n\nexport const readFiles = (dir: string): string"
},
{
"path": "packages/guess-parser/test/angular.spec.ts",
"chars": 2726,
"preview": "import { parseRoutes } from '../src/angular';\n\nconst fixtureRoutes = new Set([\n '/foo',\n '/foo/baz',\n '/foo/index',\n "
},
{
"path": "packages/guess-parser/test/detect.spec.ts",
"chars": 1320,
"preview": "import { detect } from '../src/detector';\nimport { ProjectType } from '../../common/interfaces';\n\ndescribe('detect', () "
},
{
"path": "packages/guess-parser/test/fixtures/angular/.angular-cli.json",
"chars": 1242,
"preview": "{\n \"$schema\": \"./node_modules/@angular/cli/lib/config/schema.json\",\n \"project\": {\n \"name\": \"angular\"\n },\n \"apps\":"
},
{
"path": "packages/guess-parser/test/fixtures/angular/library/index.ts",
"chars": 41,
"preview": "export * from './nested/library.module';\n"
},
{
"path": "packages/guess-parser/test/fixtures/angular/library/library.module.ts",
"chars": 389,
"preview": "import { NgModule, Component } from '@angular/core';\nimport { RouterModule } from '@angular/router';\n\n@Component({\n\n})\nc"
},
{
"path": "packages/guess-parser/test/fixtures/angular/library/nested/library.module.ts",
"chars": 389,
"preview": "import { NgModule, Component } from '@angular/core';\nimport { RouterModule } from '@angular/router';\n\n@Component({\n\n})\nc"
},
{
"path": "packages/guess-parser/test/fixtures/angular/library/tsconfig.json",
"chars": 166,
"preview": "{\n \"extends\": \"../tsconfig.json\",\n \"compilerOptions\": {\n \"outDir\": \"../out-tsc/library\",\n \"module\": \"es2015\",\n "
},
{
"path": "packages/guess-parser/test/fixtures/angular/package.json",
"chars": 1292,
"preview": "{\n \"name\": \"angular\",\n \"version\": \"0.0.0\",\n \"license\": \"MIT\",\n \"scripts\": {\n \"ng\": \"ng\",\n \"start\": \"ng serve\","
},
{
"path": "packages/guess-parser/test/fixtures/angular/src/app/about/about-routing.module.ts",
"chars": 247,
"preview": "import { NgModule } from '@angular/core';\nimport { Routes, RouterModule } from '@angular/router';\n\nconst routes: Routes "
},
{
"path": "packages/guess-parser/test/fixtures/angular/src/app/about/about.module.ts",
"chars": 275,
"preview": "import { NgModule } from '@angular/core';\nimport { CommonModule } from '@angular/common';\n\nimport { AboutRoutingModule }"
},
{
"path": "packages/guess-parser/test/fixtures/angular/src/app/app-routing.module.ts",
"chars": 1517,
"preview": "import { NgModule } from '@angular/core';\nimport { Routes, RouterModule } from '@angular/router';\nimport { BarSimpleComp"
},
{
"path": "packages/guess-parser/test/fixtures/angular/src/app/app.component.css",
"chars": 0,
"preview": ""
},
{
"path": "packages/guess-parser/test/fixtures/angular/src/app/app.component.html",
"chars": 131,
"preview": "<a routerLink=\"/foo\">Foo</a>\n<a routerLink=\"/bar-simple\">Bar</a>\n<a routerLink=\"/bar/baz\">Baz</a>\n\n<router-outlet></rout"
},
{
"path": "packages/guess-parser/test/fixtures/angular/src/app/app.component.spec.ts",
"chars": 986,
"preview": "import { TestBed, async } from '@angular/core/testing';\nimport { AppComponent } from './app.component';\ndescribe('AppCom"
},
{
"path": "packages/guess-parser/test/fixtures/angular/src/app/app.component.ts",
"chars": 207,
"preview": "import { Component } from '@angular/core';\n\n@Component({\n selector: 'app-root',\n templateUrl: './app.component.html',\n"
},
{
"path": "packages/guess-parser/test/fixtures/angular/src/app/app.module.ts",
"chars": 372,
"preview": "import { BrowserModule } from '@angular/platform-browser';\nimport { NgModule } from '@angular/core';\n\nimport { AppCompon"
},
{
"path": "packages/guess-parser/test/fixtures/angular/src/app/bar/bar.module.ts",
"chars": 299,
"preview": "import { NgModule } from '@angular/core';\nimport { RouterModule } from '@angular/router';\n\n@NgModule({\n imports: [\n "
},
{
"path": "packages/guess-parser/test/fixtures/angular/src/app/bar/baz/baz.module.ts",
"chars": 467,
"preview": "import { NgModule, Component } from '@angular/core';\nimport { Routes, RouterModule } from '@angular/router';\nimport './c"
},
{
"path": "packages/guess-parser/test/fixtures/angular/src/app/bar/baz/cycle-parent.ts",
"chars": 23,
"preview": "import './baz.module';\n"
},
{
"path": "packages/guess-parser/test/fixtures/angular/src/app/bar-simple.component.ts",
"chars": 149,
"preview": "import { Component } from '@angular/core';\n\n@Component({\n selector: 'app-component',\n template: 'Bar Simple'\n})\nexport"
},
{
"path": "packages/guess-parser/test/fixtures/angular/src/app/foo/baz/baz-routing.module.ts",
"chars": 400,
"preview": "import { NgModule } from '@angular/core';\nimport { Routes, RouterModule } from '@angular/router';\nimport { BazComponent "
},
{
"path": "packages/guess-parser/test/fixtures/angular/src/app/foo/baz/baz.component.ts",
"chars": 140,
"preview": "import { Component } from '@angular/core';\n\n@Component({\n selector: 'app-baz',\n template: 'baz-component'\n})\nexport cl"
},
{
"path": "packages/guess-parser/test/fixtures/angular/src/app/foo/baz/baz.module.ts",
"chars": 280,
"preview": "import { NgModule } from '@angular/core';\nimport { BazComponent } from './baz.component';\nimport { BazRoutingModule } fr"
},
{
"path": "packages/guess-parser/test/fixtures/angular/src/app/foo/foo-routing.module.ts",
"chars": 760,
"preview": "import { NgModule } from '@angular/core';\nimport { Routes, RouterModule } from '@angular/router';\nimport { FooComponent "
},
{
"path": "packages/guess-parser/test/fixtures/angular/src/app/foo/foo.component.ts",
"chars": 140,
"preview": "import { Component } from '@angular/core';\n\n@Component({\n selector: 'app-foo',\n template: 'foo-component'\n})\nexport cl"
},
{
"path": "packages/guess-parser/test/fixtures/angular/src/app/foo/foo.module.ts",
"chars": 291,
"preview": "import { NgModule, Component } from '@angular/core';\nimport { FooComponent } from './foo.component';\nimport { FooRouting"
},
{
"path": "packages/guess-parser/test/fixtures/angular/src/app/lazy/lazy-routing.module.ts",
"chars": 335,
"preview": "import { NgModule } from '@angular/core';\nimport { Routes, RouterModule } from '@angular/router';\n\nimport { LazyComponen"
},
{
"path": "packages/guess-parser/test/fixtures/angular/src/app/lazy/lazy.component.ts",
"chars": 107,
"preview": "import { Component } from '@angular/core';\n\n@Component({\n template: '',\n})\nexport class LazyComponent { }\n"
},
{
"path": "packages/guess-parser/test/fixtures/angular/src/app/lazy/lazy.module.ts",
"chars": 342,
"preview": "import { NgModule } from '@angular/core';\nimport { CommonModule } from '@angular/common';\n\nimport { LazyRoutingModule } "
},
{
"path": "packages/guess-parser/test/fixtures/angular/src/app/qux/qux.module.ts",
"chars": 370,
"preview": "import { NgModule, Component } from '@angular/core';\nimport { RouterModule } from '@angular/router';\n\n@Component({\n\n})\nc"
},
{
"path": "packages/guess-parser/test/fixtures/angular/src/app/wrapper/wrapper.module.ts",
"chars": 249,
"preview": "import { NgModule } from '@angular/core';\nimport { LibraryModule } from '~library';\n\n@NgModule({\n imports: [LibraryModu"
},
{
"path": "packages/guess-parser/test/fixtures/angular/src/environments/environment.prod.ts",
"chars": 51,
"preview": "export const environment = {\n production: true\n};\n"
},
{
"path": "packages/guess-parser/test/fixtures/angular/src/environments/environment.ts",
"chars": 387,
"preview": "// The file contents for the current environment will overwrite these during build.\n// The build system defaults to the "
},
{
"path": "packages/guess-parser/test/fixtures/angular/src/index.html",
"chars": 294,
"preview": "<!doctype html>\n<html lang=\"en\">\n<head>\n <meta charset=\"utf-8\">\n <title>Angular</title>\n <base href=\"/\">\n\n <meta nam"
},
{
"path": "packages/guess-parser/test/fixtures/angular/src/main.ts",
"chars": 370,
"preview": "import { enableProdMode } from '@angular/core';\nimport { platformBrowserDynamic } from '@angular/platform-browser-dynami"
},
{
"path": "packages/guess-parser/test/fixtures/angular/src/polyfills.ts",
"chars": 3114,
"preview": "/**\n * This file includes polyfills needed by Angular and is loaded before the app.\n * You can add your own extra polyfi"
},
{
"path": "packages/guess-parser/test/fixtures/angular/src/styles.css",
"chars": 80,
"preview": "/* You can add global styles to this file, and also import other style files */\n"
},
{
"path": "packages/guess-parser/test/fixtures/angular/src/test.ts",
"chars": 642,
"preview": "// This file is required by karma.conf.js and loads recursively all the .spec and framework files\n\nimport 'zone.js/dist/"
},
{
"path": "packages/guess-parser/test/fixtures/angular/src/tsconfig.app.json",
"chars": 190,
"preview": "{\n \"extends\": \"../tsconfig.json\",\n \"compilerOptions\": {\n \"outDir\": \"../out-tsc/app\",\n \"module\": \"es2015\",\n \"t"
},
{
"path": "packages/guess-parser/test/fixtures/angular/src/tsconfig.spec.json",
"chars": 262,
"preview": "{\n \"extends\": \"../tsconfig.json\",\n \"compilerOptions\": {\n \"outDir\": \"../out-tsc/spec\",\n \"module\": \"commonjs\",\n "
},
{
"path": "packages/guess-parser/test/fixtures/angular/tsconfig.json",
"chars": 445,
"preview": "{\n \"compileOnSave\": false,\n \"compilerOptions\": {\n \"outDir\": \"./dist/out-tsc\",\n \"sourceMap\": true,\n \"declarati"
},
{
"path": "packages/guess-parser/test/fixtures/gatsby/.gitignore",
"chars": 95,
"preview": "# Project dependencies\n.cache\nnode_modules\nyarn-error.log\n\n# Build directory\n/public\n.DS_Store\n"
},
{
"path": "packages/guess-parser/test/fixtures/gatsby/.prettierrc",
"chars": 69,
"preview": "{\n \"semi\": false,\n \"singleQuote\": true,\n \"trailingComma\": \"es5\"\n}\n"
},
{
"path": "packages/guess-parser/test/fixtures/gatsby/LICENSE",
"chars": 1076,
"preview": "The MIT License (MIT)\n\nCopyright (c) 2015 gatsbyjs\n\nPermission is hereby granted, free of charge, to any person obtainin"
},
{
"path": "packages/guess-parser/test/fixtures/gatsby/README.md",
"chars": 657,
"preview": "# gatsby-starter-default\nThe default Gatsby starter.\n\nFor an overview of the project structure please refer to the [Gats"
},
{
"path": "packages/guess-parser/test/fixtures/gatsby/gatsby-browser.js",
"chars": 164,
"preview": "/**\n * Implement Gatsby's Browser APIs in this file.\n *\n * See: https://www.gatsbyjs.org/docs/browser-apis/\n */\n\n // You"
},
{
"path": "packages/guess-parser/test/fixtures/gatsby/gatsby-config.js",
"chars": 124,
"preview": "module.exports = {\n siteMetadata: {\n title: 'Gatsby Default Starter',\n },\n plugins: ['gatsby-plugin-react-helmet']"
},
{
"path": "packages/guess-parser/test/fixtures/gatsby/gatsby-node.js",
"chars": 158,
"preview": "/**\n * Implement Gatsby's Node APIs in this file.\n *\n * See: https://www.gatsbyjs.org/docs/node-apis/\n */\n\n // You can d"
},
{
"path": "packages/guess-parser/test/fixtures/gatsby/gatsby-ssr.js",
"chars": 180,
"preview": "/**\n * Implement Gatsby's SSR (Server Side Rendering) APIs in this file.\n *\n * See: https://www.gatsbyjs.org/docs/ssr-ap"
},
{
"path": "packages/guess-parser/test/fixtures/gatsby/package.json",
"chars": 608,
"preview": "{\n \"name\": \"gatsby-starter-default\",\n \"description\": \"Gatsby default starter\",\n \"version\": \"1.0.0\",\n \"author\": \"Kyle"
},
{
"path": "packages/guess-parser/test/fixtures/gatsby/src/components/header.js",
"chars": 582,
"preview": "import React from 'react'\nimport Link from 'gatsby-link'\n\nconst Header = ({ siteTitle }) => (\n <div\n style={{\n "
},
{
"path": "packages/guess-parser/test/fixtures/gatsby/src/layouts/index.css",
"chars": 11244,
"preview": "html {\n font-family: sans-serif;\n -ms-text-size-adjust: 100%;\n -webkit-text-size-adjust: 100%;\n}\nbody {\n margin: 0;\n"
},
{
"path": "packages/guess-parser/test/fixtures/gatsby/src/layouts/index.js",
"chars": 853,
"preview": "import React from 'react'\nimport PropTypes from 'prop-types'\nimport Helmet from 'react-helmet'\n\nimport Header from '../c"
},
{
"path": "packages/guess-parser/test/fixtures/gatsby/src/pages/404.js",
"chars": 198,
"preview": "import React from 'react'\n\nconst NotFoundPage = () => (\n <div>\n <h1>NOT FOUND</h1>\n <p>You just hit a route that "
},
{
"path": "packages/guess-parser/test/fixtures/gatsby/src/pages/index.js",
"chars": 281,
"preview": "import React from 'react'\nimport Link from 'gatsby-link'\n\nconst IndexPage = () => (\n <div>\n <h1>Hi people</h1>\n <"
},
{
"path": "packages/guess-parser/test/fixtures/gatsby/src/pages/page-2.js",
"chars": 245,
"preview": "import React from 'react'\nimport Link from 'gatsby-link'\n\nconst SecondPage = () => (\n <div>\n <h1>Hi from the second "
},
{
"path": "packages/guess-parser/test/fixtures/ng8/.editorconfig",
"chars": 246,
"preview": "# Editor configuration, see https://editorconfig.org\nroot = true\n\n[*]\ncharset = utf-8\nindent_style = space\nindent_size ="
},
{
"path": "packages/guess-parser/test/fixtures/ng8/.gitignore",
"chars": 629,
"preview": "# See http://help.github.com/ignore-files/ for more about ignoring files.\n\n# compiled output\n/dist\n/tmp\n/out-tsc\n# Only "
},
{
"path": "packages/guess-parser/test/fixtures/ng8/README.md",
"chars": 1020,
"preview": "# Ng8\n\nThis project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 8.0.1.\n\n## Developm"
},
{
"path": "packages/guess-parser/test/fixtures/ng8/angular.json",
"chars": 3375,
"preview": "{\n \"$schema\": \"./node_modules/@angular/cli/lib/config/schema.json\",\n \"version\": 1,\n \"newProjectRoot\": \"projects\",\n \""
},
{
"path": "packages/guess-parser/test/fixtures/ng8/browserslist",
"chars": 429,
"preview": "# This file is used by the build system to adjust CSS and JS output to support the specified browsers below.\n# For addit"
},
{
"path": "packages/guess-parser/test/fixtures/ng8/e2e/protractor.conf.js",
"chars": 810,
"preview": "// @ts-check\n// Protractor configuration file, see link for more information\n// https://github.com/angular/protractor/bl"
},
{
"path": "packages/guess-parser/test/fixtures/ng8/e2e/src/app.e2e-spec.ts",
"chars": 632,
"preview": "import { AppPage } from './app.po';\nimport { browser, logging } from 'protractor';\n\ndescribe('workspace-project App', ()"
},
{
"path": "packages/guess-parser/test/fixtures/ng8/e2e/src/app.po.ts",
"chars": 251,
"preview": "import { browser, by, element } from 'protractor';\n\nexport class AppPage {\n navigateTo() {\n return browser.get(brows"
},
{
"path": "packages/guess-parser/test/fixtures/ng8/e2e/tsconfig.json",
"chars": 214,
"preview": "{\n \"extends\": \"../tsconfig.json\",\n \"compilerOptions\": {\n \"outDir\": \"../out-tsc/e2e\",\n \"module\": \"commonjs\",\n "
},
{
"path": "packages/guess-parser/test/fixtures/ng8/karma.conf.js",
"chars": 1015,
"preview": "// Karma configuration file, see link for more information\n// https://karma-runner.github.io/1.0/config/configuration-fi"
},
{
"path": "packages/guess-parser/test/fixtures/ng8/package.json",
"chars": 1276,
"preview": "{\n \"name\": \"ng8\",\n \"version\": \"0.0.0\",\n \"scripts\": {\n \"ng\": \"ng\",\n \"start\": \"ng serve\",\n \"build\": \"ng build\""
},
{
"path": "packages/guess-parser/test/fixtures/ng8/src/app/app-routing.module.ts",
"chars": 245,
"preview": "import { NgModule } from '@angular/core';\nimport { Routes, RouterModule } from '@angular/router';\n\nconst routes: Routes "
},
{
"path": "packages/guess-parser/test/fixtures/ng8/src/app/app.component.css",
"chars": 0,
"preview": ""
},
{
"path": "packages/guess-parser/test/fixtures/ng8/src/app/app.component.html",
"chars": 1152,
"preview": "<!--The content below is only a placeholder and can be replaced.-->\n<div style=\"text-align:center\">\n <h1>\n Welcome t"
},
{
"path": "packages/guess-parser/test/fixtures/ng8/src/app/app.component.spec.ts",
"chars": 1086,
"preview": "import { TestBed, async } from '@angular/core/testing';\nimport { RouterTestingModule } from '@angular/router/testing';\ni"
},
{
"path": "packages/guess-parser/test/fixtures/ng8/src/app/app.component.ts",
"chars": 207,
"preview": "import { Component } from '@angular/core';\n\n@Component({\n selector: 'app-root',\n templateUrl: './app.component.html',\n"
},
{
"path": "packages/guess-parser/test/fixtures/ng8/src/app/app.module.ts",
"chars": 393,
"preview": "import { BrowserModule } from '@angular/platform-browser';\nimport { NgModule } from '@angular/core';\n\nimport { AppRoutin"
},
{
"path": "packages/guess-parser/test/fixtures/ng8/src/assets/.gitkeep",
"chars": 0,
"preview": ""
},
{
"path": "packages/guess-parser/test/fixtures/ng8/src/environments/environment.prod.ts",
"chars": 51,
"preview": "export const environment = {\n production: true\n};\n"
},
{
"path": "packages/guess-parser/test/fixtures/ng8/src/environments/environment.ts",
"chars": 662,
"preview": "// This file can be replaced during build by using the `fileReplacements` array.\n// `ng build --prod` replaces `environm"
},
{
"path": "packages/guess-parser/test/fixtures/ng8/src/index.html",
"chars": 290,
"preview": "<!doctype html>\n<html lang=\"en\">\n<head>\n <meta charset=\"utf-8\">\n <title>Ng8</title>\n <base href=\"/\">\n\n <meta name=\"v"
},
{
"path": "packages/guess-parser/test/fixtures/ng8/src/main.ts",
"chars": 372,
"preview": "import { enableProdMode } from '@angular/core';\nimport { platformBrowserDynamic } from '@angular/platform-browser-dynami"
},
{
"path": "packages/guess-parser/test/fixtures/ng8/src/polyfills.ts",
"chars": 2838,
"preview": "/**\n * This file includes polyfills needed by Angular and is loaded before the app.\n * You can add your own extra polyfi"
},
{
"path": "packages/guess-parser/test/fixtures/ng8/src/styles.css",
"chars": 80,
"preview": "/* You can add global styles to this file, and also import other style files */\n"
},
{
"path": "packages/guess-parser/test/fixtures/ng8/src/test.ts",
"chars": 642,
"preview": "// This file is required by karma.conf.js and loads recursively all the .spec and framework files\n\nimport 'zone.js/dist/"
},
{
"path": "packages/guess-parser/test/fixtures/ng8/tsconfig.app.json",
"chars": 210,
"preview": "{\n \"extends\": \"./tsconfig.json\",\n \"compilerOptions\": {\n \"outDir\": \"./out-tsc/app\",\n \"types\": []\n },\n \"include\""
},
{
"path": "packages/guess-parser/test/fixtures/ng8/tsconfig.json",
"chars": 438,
"preview": "{\n \"compileOnSave\": false,\n \"compilerOptions\": {\n \"baseUrl\": \"./\",\n \"outDir\": \"./dist/out-tsc\",\n \"sourceMap\":"
},
{
"path": "packages/guess-parser/test/fixtures/ng8/tsconfig.spec.json",
"chars": 270,
"preview": "{\n \"extends\": \"./tsconfig.json\",\n \"compilerOptions\": {\n \"outDir\": \"./out-tsc/spec\",\n \"types\": [\n \"jasmine\","
},
{
"path": "packages/guess-parser/test/fixtures/ng8/tslint.json",
"chars": 1985,
"preview": "{\n \"extends\": \"tslint:recommended\",\n \"rules\": {\n \"array-type\": false,\n \"arrow-parens\": false,\n \"deprecation\":"
},
{
"path": "packages/guess-parser/test/fixtures/nx/LICENSE",
"chars": 1071,
"preview": "MIT License\n\nCopyright (c) 2019 Christian Janz\n\nPermission is hereby granted, free of charge, to any person obtaining a "
},
{
"path": "packages/guess-parser/test/fixtures/nx/README.md",
"chars": 154,
"preview": "# Angular monorepo example using Nx\n\nThis app shows the process of refactoring an Angular app to a monorepo with the hel"
},
{
"path": "packages/guess-parser/test/fixtures/nx/angular.json",
"chars": 12209,
"preview": "{\n \"$schema\": \"./node_modules/@angular/cli/lib/config/schema.json\",\n \"version\": 1,\n \"newProjectRoot\": \"\",\n \"projects"
},
{
"path": "packages/guess-parser/test/fixtures/nx/apps/customers-ui-e2e/src/integration/customers.component.spec.ts",
"chars": 1006,
"preview": "describe('feat-customers', () => {\n beforeEach(() => cy.visit('/iframe.html?id=customerscomponent--primary'));\n\n it('s"
},
{
"path": "packages/guess-parser/test/fixtures/nx/apps/feat-home-e2e/cypress.json",
"chars": 456,
"preview": "{\n \"fileServerFolder\": \".\",\n \"fixturesFolder\": \"./src/fixtures\",\n \"integrationFolder\": \"./src/integration\",\n \"modify"
},
{
"path": "packages/guess-parser/test/fixtures/nx/apps/feat-home-e2e/src/fixtures/example.json",
"chars": 80,
"preview": "{\n \"name\": \"Using fixtures to represent data\",\n \"email\": \"hello@cypress.io\"\n}\n"
},
{
"path": "packages/guess-parser/test/fixtures/nx/apps/feat-home-e2e/src/integration/home.component.spec.ts",
"chars": 303,
"preview": "describe('feat-home', () => {\n beforeEach(() => cy.visit('/iframe.html?id=homecomponent--primary'));\n\n it('should disp"
},
{
"path": "packages/guess-parser/test/fixtures/nx/apps/feat-home-e2e/src/plugins/index.js",
"chars": 811,
"preview": "// ***********************************************************\n// This example plugins/index.js can be used to load plug"
},
{
"path": "packages/guess-parser/test/fixtures/nx/apps/feat-home-e2e/src/support/commands.ts",
"chars": 1117,
"preview": "// ***********************************************\n// This example commands.js shows you how to\n// create various custom"
},
{
"path": "packages/guess-parser/test/fixtures/nx/apps/feat-home-e2e/src/support/index.ts",
"chars": 599,
"preview": "// ***********************************************************\n// This example support/index.js is processed and\n// load"
},
{
"path": "packages/guess-parser/test/fixtures/nx/apps/feat-home-e2e/tsconfig.e2e.json",
"chars": 152,
"preview": "{\n \"extends\": \"./tsconfig.json\",\n \"compilerOptions\": {\n \"sourceMap\": false,\n \"outDir\": \"../../dist/out-tsc\"\n },"
},
{
"path": "packages/guess-parser/test/fixtures/nx/apps/feat-home-e2e/tsconfig.json",
"chars": 126,
"preview": "{\n \"extends\": \"../../tsconfig.json\",\n \"compilerOptions\": {\n \"types\": [\"cypress\", \"node\"]\n },\n \"include\": [\"**/*.t"
},
{
"path": "packages/guess-parser/test/fixtures/nx/apps/feat-home-e2e/tslint.json",
"chars": 42,
"preview": "{\"extends\":\"../../tslint.json\",\"rules\":[]}"
},
{
"path": "packages/guess-parser/test/fixtures/nx/apps/ng-cli-app/browserslist",
"chars": 429,
"preview": "# This file is used by the build system to adjust CSS and JS output to support the specified browsers below.\n# For addit"
},
{
"path": "packages/guess-parser/test/fixtures/nx/apps/ng-cli-app/karma.conf.js",
"chars": 1020,
"preview": "// Karma configuration file, see link for more information\n// https://karma-runner.github.io/1.0/config/configuration-fi"
},
{
"path": "packages/guess-parser/test/fixtures/nx/apps/ng-cli-app/src/app/app.component.html",
"chars": 260,
"preview": "<app-navigation>\n <header>\n <ng-container *ngIf=\"user$ | async as user\">\n <span>{{ user.fullName }}</span>\n "
},
{
"path": "packages/guess-parser/test/fixtures/nx/apps/ng-cli-app/src/app/app.component.scss",
"chars": 0,
"preview": ""
},
{
"path": "packages/guess-parser/test/fixtures/nx/apps/ng-cli-app/src/app/app.component.spec.ts",
"chars": 794,
"preview": "import { TestBed, async } from '@angular/core/testing';\nimport { RouterTestingModule } from '@angular/router/testing';\ni"
},
{
"path": "packages/guess-parser/test/fixtures/nx/apps/ng-cli-app/src/app/app.component.ts",
"chars": 623,
"preview": "import { IdleMonitorService } from '@scullyio/ng-lib';\nimport { Component, OnInit } from '@angular/core';\nimport { Obser"
},
{
"path": "packages/guess-parser/test/fixtures/nx/apps/ng-cli-app/src/app/app.module.ts",
"chars": 1281,
"preview": "import { BrowserModule } from '@angular/platform-browser';\nimport { NgModule } from '@angular/core';\n\nimport { AppCompon"
},
{
"path": "packages/guess-parser/test/fixtures/nx/apps/ng-cli-app/src/assets/.gitkeep",
"chars": 0,
"preview": ""
},
{
"path": "packages/guess-parser/test/fixtures/nx/apps/ng-cli-app/src/assets/customers.json",
"chars": 10410,
"preview": "[\n {\n \"id\": 1,\n \"first_name\": \"Leigha\",\n \"last_name\": \"Waulker\",\n \"email\": \"lwaulker0@disqus.com\",\n \"str"
},
{
"path": "packages/guess-parser/test/fixtures/nx/apps/ng-cli-app/src/environments/environment.prod.ts",
"chars": 51,
"preview": "export const environment = {\n production: true\n};\n"
},
{
"path": "packages/guess-parser/test/fixtures/nx/apps/ng-cli-app/src/environments/environment.ts",
"chars": 662,
"preview": "// This file can be replaced during build by using the `fileReplacements` array.\n// `ng build --prod` replaces `environm"
},
{
"path": "packages/guess-parser/test/fixtures/nx/apps/ng-cli-app/src/index.html",
"chars": 556,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n <head>\n <meta charset=\"utf-8\" />\n <title>Demo App</title>\n <base href=\"/\" />"
},
{
"path": "packages/guess-parser/test/fixtures/nx/apps/ng-cli-app/src/main.ts",
"chars": 394,
"preview": "import 'hammerjs';\nimport { enableProdMode } from '@angular/core';\nimport { platformBrowserDynamic } from '@angular/plat"
},
{
"path": "packages/guess-parser/test/fixtures/nx/apps/ng-cli-app/src/polyfills.ts",
"chars": 3033,
"preview": "/**\n * This file includes polyfills needed by Angular and is loaded before the app.\n * You can add your own extra polyfi"
},
{
"path": "packages/guess-parser/test/fixtures/nx/apps/ng-cli-app/src/styles.scss",
"chars": 187,
"preview": "/* You can add global styles to this file, and also import other style files */\n\nhtml,\nbody {\n height: 100%;\n}\nbody {\n "
},
{
"path": "packages/guess-parser/test/fixtures/nx/apps/ng-cli-app/src/test.ts",
"chars": 642,
"preview": "// This file is required by karma.conf.js and loads recursively all the .spec and framework files\n\nimport 'zone.js/dist/"
},
{
"path": "packages/guess-parser/test/fixtures/nx/apps/ng-cli-app/tsconfig.app.json",
"chars": 247,
"preview": "{\n \"extends\": \"../../tsconfig.json\",\n \"compilerOptions\": {\n \"outDir\": \"../../dist/out-tsc\",\n \"types\": []\n },\n "
},
{
"path": "packages/guess-parser/test/fixtures/nx/apps/ng-cli-app/tsconfig.spec.json",
"chars": 236,
"preview": "{\n \"extends\": \"../../tsconfig.json\",\n \"compilerOptions\": {\n \"outDir\": \"../../dist/out-tsc\",\n \"types\": [\"jasmine\""
},
{
"path": "packages/guess-parser/test/fixtures/nx/apps/ng-cli-app-e2e/cypress.json",
"chars": 390,
"preview": "{\n \"fileServerFolder\": \"./\",\n \"fixturesFolder\": \"./src/fixtures\",\n \"integrationFolder\": \"./src/integration\",\n \"plugi"
},
{
"path": "packages/guess-parser/test/fixtures/nx/apps/ng-cli-app-e2e/src/fixtures/example.json",
"chars": 80,
"preview": "{\n \"name\": \"Using fixtures to represent data\",\n \"email\": \"hello@cypress.io\"\n}\n"
},
{
"path": "packages/guess-parser/test/fixtures/nx/apps/ng-cli-app-e2e/src/integration/app.spec.ts",
"chars": 721,
"preview": "describe('app', () => {\n beforeEach(() => cy.visit('/'));\n\n it('should list all nav items', () => {\n cy.findByText("
},
{
"path": "packages/guess-parser/test/fixtures/nx/apps/ng-cli-app-e2e/src/integration/customers.spec.ts",
"chars": 929,
"preview": "describe('customers', () => {\n beforeEach(() => {\n cy.login('test', 'goodpass');\n cy.findByText('Customers').clic"
},
{
"path": "packages/guess-parser/test/fixtures/nx/apps/ng-cli-app-e2e/src/integration/home.spec.ts",
"chars": 276,
"preview": "describe('home', () => {\n beforeEach(() => cy.login('test', 'goodpass'));\n\n it('should display correctly', () => {\n "
},
{
"path": "packages/guess-parser/test/fixtures/nx/apps/ng-cli-app-e2e/src/plugins/index.js",
"chars": 785,
"preview": "const { preprocessTypescript } = require('@nrwl/cypress/plugins/preprocessor'); // *************************************"
},
{
"path": "packages/guess-parser/test/fixtures/nx/apps/ng-cli-app-e2e/src/support/app.po.ts",
"chars": 47,
"preview": "export const getGreeting = () => cy.get('h1');\n"
},
{
"path": "packages/guess-parser/test/fixtures/nx/apps/ng-cli-app-e2e/src/support/commands.ts",
"chars": 1289,
"preview": "// ***********************************************\n// This example commands.js shows you how to\n// create various custom"
},
{
"path": "packages/guess-parser/test/fixtures/nx/apps/ng-cli-app-e2e/src/support/index.ts",
"chars": 599,
"preview": "// ***********************************************************\n// This example support/index.js is processed and\n// load"
},
{
"path": "packages/guess-parser/test/fixtures/nx/apps/ng-cli-app-e2e/tsconfig.e2e.json",
"chars": 152,
"preview": "{\n \"extends\": \"./tsconfig.json\",\n \"compilerOptions\": {\n \"sourceMap\": false,\n \"outDir\": \"../../dist/out-tsc\"\n },"
},
{
"path": "packages/guess-parser/test/fixtures/nx/apps/ng-cli-app-e2e/tsconfig.json",
"chars": 161,
"preview": "{\n \"extends\": \"../../tsconfig.json\",\n \"compilerOptions\": {\n \"types\": [\"cypress\", \"@types/testing-library__cypress\","
},
{
"path": "packages/guess-parser/test/fixtures/nx/apps/shared-components-e2e/cypress.json",
"chars": 472,
"preview": "{\n \"fileServerFolder\": \".\",\n \"fixturesFolder\": \"./src/fixtures\",\n \"integrationFolder\": \"./src/integration\",\n \"modify"
},
{
"path": "packages/guess-parser/test/fixtures/nx/apps/shared-components-e2e/src/fixtures/example.json",
"chars": 80,
"preview": "{\n \"name\": \"Using fixtures to represent data\",\n \"email\": \"hello@cypress.io\"\n}\n"
},
{
"path": "packages/guess-parser/test/fixtures/nx/apps/shared-components-e2e/src/integration/info-box/info-box.component.spec.ts",
"chars": 274,
"preview": "describe('shared-components', () => {\n it('should render the component', () => {\n cy.visit(\n '/iframe.html?id=i"
},
{
"path": "packages/guess-parser/test/fixtures/nx/apps/shared-components-e2e/src/integration/navigation/navigation.component.spec.ts",
"chars": 217,
"preview": "describe('shared-components', () => {\n beforeEach(() => cy.visit('/iframe.html?id=navigationcomponent--primary'));\n\n i"
},
{
"path": "packages/guess-parser/test/fixtures/nx/apps/shared-components-e2e/src/plugins/index.js",
"chars": 811,
"preview": "// ***********************************************************\n// This example plugins/index.js can be used to load plug"
},
{
"path": "packages/guess-parser/test/fixtures/nx/apps/shared-components-e2e/src/support/commands.ts",
"chars": 1068,
"preview": "// ***********************************************\n// This example commands.js shows you how to\n// create various custom"
},
{
"path": "packages/guess-parser/test/fixtures/nx/apps/shared-components-e2e/src/support/index.ts",
"chars": 599,
"preview": "// ***********************************************************\n// This example support/index.js is processed and\n// load"
},
{
"path": "packages/guess-parser/test/fixtures/nx/apps/shared-components-e2e/tsconfig.e2e.json",
"chars": 152,
"preview": "{\n \"extends\": \"./tsconfig.json\",\n \"compilerOptions\": {\n \"sourceMap\": false,\n \"outDir\": \"../../dist/out-tsc\"\n },"
},
{
"path": "packages/guess-parser/test/fixtures/nx/apps/shared-components-e2e/tsconfig.json",
"chars": 126,
"preview": "{\n \"extends\": \"../../tsconfig.json\",\n \"compilerOptions\": {\n \"types\": [\"cypress\", \"node\"]\n },\n \"include\": [\"**/*.t"
},
{
"path": "packages/guess-parser/test/fixtures/nx/apps/shared-components-e2e/tslint.json",
"chars": 42,
"preview": "{\"extends\":\"../../tslint.json\",\"rules\":[]}"
},
{
"path": "packages/guess-parser/test/fixtures/nx/docs/_config.yml",
"chars": 69,
"preview": "theme: jekyll-theme-minimal\ntitle: Angular monorepo example using Nx\n"
},
{
"path": "packages/guess-parser/test/fixtures/nx/docs/index.md",
"chars": 323,
"preview": "---\nlayout: default\n---\n\n# Angular monorepo example using Nx\n\nThis app shows the process of refactoring an Angular app t"
}
]
// ... and 252 more files (download for full content)
About this extraction
This page contains the full source code of the guess-js/guess GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 452 files (630.1 KB), approximately 179.6k tokens, and a symbol index with 250 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.