@segment/snippet
Advanced tools
| const f = require('./index') | ||
| console.log(f.min()) |
+70
| # snippet | ||
| Render the analytics.js snippet. | ||
| The recommended way to use analytics.js is to follow the [analytics.js quickstart guide](https://segment.com/docs/connections/sources/catalog/libraries/website/javascript/quickstart/). If you absolutely need to generate a snippet dynamically, this is an alternate solution. Note that when using this in-browser, the global `analytics` object will not be defined until the snippet is rendered and executed. | ||
| For information on browser support, see: https://segment.com/docs/connections/sources/catalog/libraries/website/javascript/supported-browsers/ | ||
| ## Installation | ||
| ```sh | ||
| # npm | ||
| npm install @segment/snippet | ||
| # yarn | ||
| yarn add @segment/snippet | ||
| ``` | ||
| ## Example | ||
| ```js | ||
| const snippet = require('@segment/snippet'); | ||
| const contents = snippet.max({ | ||
| host: 'cdn.segment.com', | ||
| apiKey: '03fwkuu3', | ||
| page: { | ||
| category: 'Docs', | ||
| name: 'Integrations', | ||
| properties: { | ||
| foo: 'bar' | ||
| } | ||
| } | ||
| }); | ||
| ``` | ||
| ## API | ||
| ### snippet.max(options) | ||
| Returns the maxified version of the analytics.js snippet given a set of `options`: | ||
| * `host`: the domain name where the analytics.js script is hosted. | ||
| * `useHostForBundles`: If set to `true`, the snippet will include the `_cdn` property to tell analytics.js where to fetch bundles from. | ||
| * `apiKey`: the `apiKey` to load in the snippet. | ||
| * `page`: the options to pass to `analytics.page`. if `page` is `false`, then the `page()` call will be omitted. | ||
| * `load`: If object, these are the settings passed as the second argument to analytics.load. This can be useful if you want to override Segment.io integration behavior, or if you want dynamically control which integraions load on the client-side for things like GDPR. If set to `false` the `load()` call will be omitted. | ||
| * `ajsPath`: override the default analytics.min.js location | ||
| ### snippet.min(options) | ||
| Returns the minified version of the snippet. | ||
| ## Development | ||
| ### Installation + Running Tests | ||
| ``` | ||
| nvm use | ||
| make install | ||
| make test | ||
| ``` | ||
| ## Publishing to `npm` | ||
| ``` | ||
| git co master | ||
| make build | ||
| npm version <patch|minor|major> | ||
| git push --follow-tags | ||
| npm publish | ||
| ``` |
+30
-27
@@ -5,38 +5,41 @@ version: 2 | ||
| docker: | ||
| - image: circleci/node:8-browsers | ||
| - image: circleci/node:14-browsers | ||
| steps: | ||
| - checkout | ||
| - run: npm config set "//registry.npmjs.org/:_authToken" $NPM_AUTH | ||
| - run: sudo npm -g install codecov | ||
| - run: make install | ||
| - run: make test | ||
| - run: codecov | ||
| environment: | ||
| PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 | ||
| NPM_CONFIG_PROGRESS: false | ||
| NPM_CONFIG_SPIN: false | ||
| TEST_REPORTS_DIR: $CIRCLE_TEST_REPORTS | ||
| publish: | ||
| docker: | ||
| - image: circleci/node:8-browsers | ||
| steps: | ||
| - checkout | ||
| - run: make build | ||
| - run: npm config set "//registry.npmjs.org/:_authToken" $NPM_AUTH | ||
| - run: npm publish . | ||
| workflows: | ||
| version: 2 | ||
| test_and_publish: | ||
| jobs: | ||
| - build: | ||
| filters: | ||
| tags: | ||
| only: /.*/ | ||
| - publish: | ||
| requires: | ||
| - build | ||
| filters: | ||
| tags: | ||
| only: /[0-9]+(\.[0-9]+)*(-.+)?/ | ||
| branches: | ||
| ignore: /.*/ | ||
| # Disabling because people seem to be publishing manually for the last few years. | ||
| # We are no longer using CircleCI as a company (and I think we'd need a to set a new npm automation token). | ||
| # publish: | ||
| # docker: | ||
| # - image: circleci/node:14-browsers | ||
| # steps: | ||
| # - checkout | ||
| # - run: make build | ||
| # - run: npm config set "//registry.npmjs.org/:_authToken" $NPM_AUTH | ||
| # - run: npm publish . | ||
| # workflows: | ||
| # version: 2 | ||
| # test_and_publish: | ||
| # jobs: | ||
| # - build: | ||
| # filters: | ||
| # tags: | ||
| # only: /.*/ | ||
| # - publish: | ||
| # requires: | ||
| # - build | ||
| # filters: | ||
| # tags: | ||
| # only: /[0-9]+(\.[0-9]+)*(-.+)?/ | ||
| # branches: | ||
| # ignore: /.*/ |
| module.exports=function(settings) { | ||
| var __t, __p = ''; | ||
| __p += '(function(){\n\n // Create a queue, but don\'t obliterate an existing one!\n var analytics = window.analytics = window.analytics || [];\n\n // If the real analytics.js is already on the page return.\n if (analytics.initialize) return;\n\n // If the snippet was invoked already show an error.\n if (analytics.invoked) {\n if (window.console && console.error) {\n console.error(\'Segment snippet included twice.\');\n }\n return;\n }\n\n // Invoked flag, to make sure the snippet\n // is never invoked twice.\n analytics.invoked = true;\n\n // A list of the methods in Analytics.js to stub.\n analytics.methods = [\n \'trackSubmit\',\n \'trackClick\',\n \'trackLink\',\n \'trackForm\',\n \'pageview\',\n \'identify\',\n \'reset\',\n \'group\',\n \'track\',\n \'ready\',\n \'alias\',\n \'debug\',\n \'page\',\n \'once\',\n \'off\',\n \'on\',\n \'addSourceMiddleware\',\n \'addIntegrationMiddleware\',\n \'setAnonymousId\',\n \'addDestinationMiddleware\'\n ];\n\n // Define a factory to create stubs. These are placeholders\n // for methods in Analytics.js so that you never have to wait\n // for it to load to actually record data. The `method` is\n // stored as the first argument, so we can replay the data.\n analytics.factory = function(method){\n return function(){\n if (window.analytics.initialized) {\n // Sometimes users assigned analytics to a variable before analytics is done loading, resulting in a stale reference.\n // If so, proxy any calls to the \'real\' analytics instance.\n return window.analytics[method].apply(window.analytics, arguments);\n }\n var args = Array.prototype.slice.call(arguments);\n args.unshift(method);\n analytics.push(args);\n return analytics;\n };\n };\n\n // For each of our methods, generate a queueing stub.\n for (var i = 0; i < analytics.methods.length; i++) {\n var key = analytics.methods[i];\n analytics[key] = analytics.factory(key);\n }\n\n // Define a method to load Analytics.js from our CDN,\n // and that will be sure to only ever load it once.\n analytics.load = function(key, options){\n // Create an async script element based on your key.\n var script = document.createElement(\'script\');\n script.type = \'text/javascript\';\n script.async = true;\n script.src = "https://' + | ||
| __p += '(function(){\n\n // Create a queue, but don\'t obliterate an existing one!\n var analytics = window.analytics = window.analytics || [];\n\n // If the real analytics.js is already on the page return.\n if (analytics.initialize) return;\n\n // If the snippet was invoked already show an error.\n if (analytics.invoked) {\n if (window.console && console.error) {\n console.error(\'Segment snippet included twice.\');\n }\n return;\n }\n\n // Invoked flag, to make sure the snippet\n // is never invoked twice.\n analytics.invoked = true;\n\n // A list of the methods in Analytics.js to stub.\n analytics.methods = [\n \'trackSubmit\',\n \'trackClick\',\n \'trackLink\',\n \'trackForm\',\n \'pageview\',\n \'identify\',\n \'reset\',\n \'group\',\n \'track\',\n \'ready\',\n \'alias\',\n \'debug\',\n \'page\',\n \'once\',\n \'off\',\n \'on\',\n \'addSourceMiddleware\',\n \'addIntegrationMiddleware\',\n \'setAnonymousId\',\n \'addDestinationMiddleware\'\n ];\n\n // Define a factory to create stubs. These are placeholders\n // for methods in Analytics.js so that you never have to wait\n // for it to load to actually record data. The `method` is\n // stored as the first argument, so we can replay the data.\n analytics.factory = function(e){\n return function(){\n if (window.analytics.initialized) {\n // Sometimes users assigned analytics to a variable before analytics is done loading, resulting in a stale reference.\n // If so, proxy any calls to the \'real\' analytics instance.\n return window.analytics[e].apply(window.analytics, arguments);\n }\n var args = Array.prototype.slice.call(arguments);\n args.unshift(e);\n analytics.push(args);\n return analytics;\n };\n };\n\n\n // For each of our methods, generate a queueing stub.\n for (var i = 0; i < analytics.methods.length; i++) {\n var key = analytics.methods[i];\n analytics[key] = analytics.factory(key);\n }\n\n // Define a method to load Analytics.js from our CDN,\n // and that will be sure to only ever load it once.\n analytics.load = function(key, options){\n // Create an async script element based on your key.\n var t = document.createElement(\'script\');\n t.type = \'text/javascript\';\n t.async = true;\n t.src = "https://' + | ||
| ((__t = ( settings.host )) == null ? '' : __t) + | ||
| '' + | ||
| ((__t = ( settings.ajsPath )) == null ? '' : __t) + | ||
| '";\n\n // Insert our script next to the first script element.\n var first = document.getElementsByTagName(\'script\')[0];\n first.parentNode.insertBefore(script, first);\n analytics._loadOptions = options;\n };\n analytics._writeKey = \'' + | ||
| '";\n\n // Insert our script next to the first script element.\n var first = document.getElementsByTagName(\'script\')[0];\n first.parentNode.insertBefore(t, first);\n analytics._loadOptions = options;\n };\n analytics._writeKey = \'' + | ||
| ((__t = ( settings.apiKey )) == null ? '' : __t) + | ||
@@ -9,0 +9,0 @@ '\';\n\n ' + |
| module.exports=function(settings) { | ||
| var __t, __p = ''; | ||
| __p += '!function(){var analytics=window.analytics=window.analytics||[];if(!analytics.initialize)if(analytics.invoked)window.console&&console.error&&console.error("Segment snippet included twice.");else{analytics.invoked=!0;analytics.methods=["trackSubmit","trackClick","trackLink","trackForm","pageview","identify","reset","group","track","ready","alias","debug","page","once","off","on","addSourceMiddleware","addIntegrationMiddleware","setAnonymousId","addDestinationMiddleware"];analytics.factory=function(t){return function(){if(window.analytics.initialized)return window.analytics[t].apply(window.analytics,arguments);var e=Array.prototype.slice.call(arguments);e.unshift(t);analytics.push(e);return analytics}};for(var t=0;t<analytics.methods.length;t++){var key=analytics.methods[t];analytics[key]=analytics.factory(key)}analytics.load=function(key,t){var e=document.createElement("script");e.type="text/javascript";e.async=!0;e.src="https://' + | ||
| __p += '!function(){var analytics=window.analytics=window.analytics||[];if(!analytics.initialize)if(analytics.invoked)window.console&&console.error&&console.error("Segment snippet included twice.");else{analytics.invoked=!0;analytics.methods=["trackSubmit","trackClick","trackLink","trackForm","pageview","identify","reset","group","track","ready","alias","debug","page","once","off","on","addSourceMiddleware","addIntegrationMiddleware","setAnonymousId","addDestinationMiddleware"];analytics.factory=function(e){return function(){if(window.analytics.initialized)return window.analytics[e].apply(window.analytics,arguments);var i=Array.prototype.slice.call(arguments);i.unshift(e);analytics.push(i);return analytics}};for(var i=0;i<analytics.methods.length;i++){var key=analytics.methods[i];analytics[key]=analytics.factory(key)}analytics.load=function(key,i){var t=document.createElement("script");t.type="text/javascript";t.async=!0;t.src="https://' + | ||
| ((__t = ( settings.host )) == null ? '' : __t) + | ||
| '' + | ||
| ((__t = ( settings.ajsPath )) == null ? '' : __t) + | ||
| '";var i=document.getElementsByTagName("script")[0];i.parentNode.insertBefore(e,i);analytics._loadOptions=t};analytics._writeKey="' + | ||
| '";var n=document.getElementsByTagName("script")[0];n.parentNode.insertBefore(t,n);analytics._loadOptions=i};analytics._writeKey="' + | ||
| ((__t = ( settings.apiKey )) == null ? '' : __t) + | ||
@@ -9,0 +9,0 @@ '";' + |
+1
-6
@@ -36,7 +36,2 @@ /* eslint-env node */ | ||
| }, | ||
| sl_ie_10: { | ||
| base: 'SauceLabs', | ||
| browserName: 'internet explorer', | ||
| version: '10' | ||
| }, | ||
| sl_ie_11: { | ||
@@ -67,3 +62,3 @@ base: 'SauceLabs', | ||
| browsers: ['PhantomJS'].concat(Object.keys(customLaunchers)), | ||
| browsers: Object.keys(customLaunchers), | ||
@@ -70,0 +65,0 @@ customLaunchers: customLaunchers, |
+9
-27
| /* eslint-env node */ | ||
| 'use strict'; | ||
| const { chromium } = require('playwright-chromium') | ||
| process.env.CHROME_BIN = chromium.executablePath() | ||
| module.exports = function(config) { | ||
| config.set({ | ||
| autoWatch: false, | ||
| singleRun: true, | ||
| files: [ | ||
@@ -12,12 +17,11 @@ // https://app.segment.com/segment-libraries/sources/snippet/settings/keys | ||
| browsers: ['PhantomJS'], | ||
| browsers: ['ChromeHeadless'], | ||
| frameworks: ['browserify', 'mocha'], | ||
| reporters: ['spec', 'coverage'], | ||
| reporters: ['spec'], | ||
| preprocessors: { | ||
| 'test/**/*.js': 'browserify' | ||
| 'test/**/*.js': ['browserify'] | ||
| }, | ||
| client: { | ||
@@ -31,24 +35,2 @@ mocha: { | ||
| browserify: { | ||
| debug: true, | ||
| transform: [ | ||
| [ | ||
| 'browserify-istanbul', | ||
| { | ||
| instrumenterConfig: { | ||
| embedSource: true | ||
| } | ||
| } | ||
| ] | ||
| ] | ||
| }, | ||
| coverageReporter: { | ||
| reporters: [ | ||
| { type: 'text' }, | ||
| { type: 'html' }, | ||
| { type: 'json' } | ||
| ] | ||
| }, | ||
| plugins: [ | ||
@@ -55,0 +37,0 @@ 'karma-*' |
+3
-11
@@ -6,3 +6,2 @@ ## | ||
| ESLINT := node_modules/.bin/eslint | ||
| ISTANBUL := node_modules/.bin/istanbul | ||
| KARMA := node_modules/.bin/karma | ||
@@ -51,9 +50,2 @@ MATCHA := node_modules/.bin/matcha | ||
| # Istanbul flags. | ||
| COVERAGE_DIR ?= coverage | ||
| ISTANBUL_FLAGS := \ | ||
| --root "./lib" \ | ||
| --include-all-sources true \ | ||
| --dir "$(COVERAGE_DIR)/Node $(shell node -v)" | ||
| ## | ||
@@ -65,3 +57,3 @@ # Tasks | ||
| node_modules: package.json $(wildcard node_modules/*/package.json) | ||
| @npm install | ||
| @npm ci | ||
| @touch $@ | ||
@@ -102,3 +94,3 @@ | ||
| test-node: install build | ||
| @NODE_ENV=test $(ISTANBUL) cover $(ISTANBUL_FLAGS) $(_MOCHA) -- $(MOCHA_FLAGS) test/render.test.js | ||
| @NODE_ENV=test $(_MOCHA) -- $(MOCHA_FLAGS) test/render.test.js | ||
| .PHONY: test-node | ||
@@ -112,4 +104,4 @@ | ||
| # Default test target. | ||
| test: lint bench test-node test-browser | ||
| test: bench test-node test-browser | ||
| .PHONY: test | ||
| .DEFAULT_GOAL = test |
+6
-6
| { | ||
| "name": "@segment/snippet", | ||
| "author": "Segment.io <friends@segment.com>", | ||
| "version": "4.16.0", | ||
| "version": "4.16.1", | ||
| "repository": "git://github.com/segmentio/snippet.git", | ||
@@ -24,3 +24,2 @@ "description": "Templating methods for rendering the analytics.js snippet.", | ||
| "browserify": "^13.1.0", | ||
| "browserify-istanbul": "^2.0.0", | ||
| "eslint": "^4.18.2", | ||
@@ -31,9 +30,8 @@ "eslint-plugin-mocha": "^2.2.0", | ||
| "jest": "^27.2.0", | ||
| "karma": "^1.1.0", | ||
| "karma": "~2.0.0", | ||
| "karma-browserify": "^5.0.4", | ||
| "karma-chrome-launcher": "^1.0.1", | ||
| "karma-chrome-launcher": "^3.2.0", | ||
| "karma-coverage": "^1.0.0", | ||
| "karma-junit-reporter": "^1.0.0", | ||
| "karma-mocha": "1.0.1", | ||
| "karma-phantomjs-launcher": "^1.0.0", | ||
| "karma-sauce-launcher": "^1.0.0", | ||
@@ -45,9 +43,11 @@ "karma-spec-reporter": "0.0.26", | ||
| "mocha": "~1.12.1", | ||
| "playwright-chromium": "^1.32.3", | ||
| "sinon": "^1.17.5", | ||
| "start-server-and-test": "^1.14.0", | ||
| "start-server-and-test": "^2.0.0", | ||
| "ts-jest": "^27.0.5", | ||
| "typescript": "^4.4.3", | ||
| "uglify-js": "^2.8.29", | ||
| "watchify": "^3.11.1", | ||
| "webdriverio": "^7.12.5" | ||
| } | ||
| } |
+2
-2
@@ -19,4 +19,4 @@ 'use strict'; | ||
| var snippetMin = template(minify(source, { | ||
| mangle: { except: ['analytics', 'key'] }, | ||
| compress: { sequences: false, side_effects: false }, | ||
| mangle: { except: ['analytics', 'key', 't', 'e'] }, | ||
| compress: { sequences: false, side_effects: false, }, | ||
| fromString: true | ||
@@ -23,0 +23,0 @@ }).code.replace(optionalCDNRegex, '$1').replace(loadRegex, '$1').replace(pageRegex, '$1').replace(lineRegex, '\n$1').replace(versionRegex, packageJSON.version), { variable: 'settings' }); |
@@ -49,3 +49,3 @@ (function(){ | ||
| // stored as the first argument, so we can replay the data. | ||
| analytics.factory = function(method){ | ||
| analytics.factory = function(e){ | ||
| return function(){ | ||
@@ -55,6 +55,6 @@ if (window.analytics.initialized) { | ||
| // If so, proxy any calls to the 'real' analytics instance. | ||
| return window.analytics[method].apply(window.analytics, arguments); | ||
| return window.analytics[e].apply(window.analytics, arguments); | ||
| } | ||
| var args = Array.prototype.slice.call(arguments); | ||
| args.unshift(method); | ||
| args.unshift(e); | ||
| analytics.push(args); | ||
@@ -65,2 +65,3 @@ return analytics; | ||
| // For each of our methods, generate a queueing stub. | ||
@@ -76,10 +77,10 @@ for (var i = 0; i < analytics.methods.length; i++) { | ||
| // Create an async script element based on your key. | ||
| var script = document.createElement('script'); | ||
| script.type = 'text/javascript'; | ||
| script.async = true; | ||
| script.src = "https://<%= settings.host %><%= settings.ajsPath %>"; | ||
| var t = document.createElement('script'); | ||
| t.type = 'text/javascript'; | ||
| t.async = true; | ||
| t.src = "https://<%= settings.host %><%= settings.ajsPath %>"; | ||
| // Insert our script next to the first script element. | ||
| var first = document.getElementsByTagName('script')[0]; | ||
| first.parentNode.insertBefore(script, first); | ||
| first.parentNode.insertBefore(t, first); | ||
| analytics._loadOptions = options; | ||
@@ -86,0 +87,0 @@ }; |
+0
-1
@@ -12,3 +12,2 @@ declare module '@segment/snippet' { | ||
| integrations?: { | ||
| All?: boolean | ||
| [key: string]: boolean | ||
@@ -15,0 +14,0 @@ } |
-47
| [](https://circleci.com/gh/segmentio/snippet) | ||
| # snippet | ||
| Render the analytics.js snippet. | ||
| The recommended way to use analytics.js is to follow the [analytics.js quickstart guide](https://segment.com/docs/connections/sources/catalog/libraries/website/javascript/quickstart/). If you absolutely need to generate a snippet dynamically, this is an alternate solution. Note that when using this in-browser, the global `analytics` object will not be defined until the snippet is rendered and executed. | ||
| This package is supported on IE8+, Chrome, Firefox, Safari 9, Microsoft Edge, Node.js 0.10+ | ||
| For IE7 support, install a global `JSON` polyfill on the page prior to loading this package. | ||
| ## Example | ||
| ```js | ||
| var snippet = require('@segment/snippet'); | ||
| var contents = snippet.max({ | ||
| host: 'cdn.segment.com', | ||
| apiKey: '03fwkuu3', | ||
| page: { | ||
| category: 'Docs', | ||
| name: 'Integrations', | ||
| properties: { | ||
| foo: 'bar' | ||
| } | ||
| } | ||
| }); | ||
| ``` | ||
| ## API | ||
| ### snippet.max(options) | ||
| Returns the maxified version of the analytics.js snippet given a set of `options`: | ||
| * `host`: the domain name where the analytics.js script is hosted. | ||
| * `useHostForBundles`: If set to `true`, the snippet will include the `_cdn` property to tell analytics.js where to fetch bundles from. | ||
| * `apiKey`: the `apiKey` to load in the snippet. | ||
| * `page`: the options to pass to `analytics.page`. if `page` is `false`, then the `page()` call will be omitted. | ||
| * `load`: if set to `false` the `load()` call will be omitted. This is useful for if you want dynamically control the load process on the client-side for things like GDPR. | ||
| * `ajsPath`: override the default analytics.min.js location | ||
| ### snippet.min(options) | ||
| Returns the minified version of the snippet. |
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 5 instances in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 4 instances in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
19
5.56%71
47.92%1
-50%28896
-0.56%419
-4.56%10
11.11%