/** * Compiles this Soy file set into JS source code files and returns these JS files as a list of * strings, one per file. * * @param jsSrcOptions The compilation options for the JS Src output target. * @param msgBundle The bundle of translated messages, or null to use the messages from the Soy * source. * @return A list of strings where each string represents the JS source code that belongs in one * JS file. The generated JS files correspond one-to-one to the original Soy source files. * @throws SoySyntaxException If a syntax error is found. */ public List<String> compileToJsSrc(SoyJsSrcOptions jsSrcOptions, @Nullable SoyMsgBundle msgBundle) throws SoySyntaxException { boolean doEnforceSyntaxVersionV2 = !jsSrcOptions.shouldAllowDeprecatedSyntax(); SoyFileSetNode soyTree = (new SoyFileSetParser(soyFileSuppliers)) .setDoEnforceSyntaxVersionV2(doEnforceSyntaxVersionV2) .parse(); runMiddleendPasses(soyTree, doEnforceSyntaxVersionV2); return jsSrcMainProvider.get().genJsSrc(soyTree, jsSrcOptions, msgBundle); }
/** * Compiles this Soy file set into JS source code files and writes these JS files to disk. * * @param outputPathFormat The format string defining how to build the output file path * corresponding to an input file path. * @param inputFilePathPrefix The prefix prepended to all input file paths (can be empty string). * @param jsSrcOptions The compilation options for the JS Src output target. * @param locales The list of locales. Can be an empty list if not applicable. * @param messageFilePathFormat The message file path format, or null if not applicable. * @throws SoySyntaxException If a syntax error is found. * @throws IOException If there is an error in opening/reading a message file or opening/writing * an output JS file. */ void compileToJsSrcFiles( String outputPathFormat, String inputFilePathPrefix, SoyJsSrcOptions jsSrcOptions, List<String> locales, @Nullable String messageFilePathFormat) throws SoySyntaxException, IOException { boolean doEnforceSyntaxVersionV2 = !jsSrcOptions.shouldAllowDeprecatedSyntax(); SoyFileSetNode soyTree = (new SoyFileSetParser(soyFileSuppliers)) .setDoEnforceSyntaxVersionV2(doEnforceSyntaxVersionV2) .parse(); runMiddleendPasses(soyTree, doEnforceSyntaxVersionV2); if (locales.size() == 0) { // Not generating localized JS. jsSrcMainProvider .get() .genJsFiles(soyTree, jsSrcOptions, null, null, outputPathFormat, inputFilePathPrefix); } else { // Generating localized JS. for (String locale : locales) { SoyFileSetNode soyTreeClone = soyTree.clone(); String msgFilePath = JsSrcUtils.buildFilePath(messageFilePathFormat, locale, null, inputFilePathPrefix); SoyMsgBundle msgBundle = msgBundleHandlerProvider.get().createFromFile(new File(msgFilePath)); if (msgBundle.getLocaleString() == null) { // TODO: Remove this check (but make sure no projects depend on this behavior). // There was an error reading the message file. We continue processing only if the locale // begins with "en", because falling back to the Soy source will proably be fine. if (!locale.startsWith("en")) { throw new IOException("Error opening or reading message file " + msgFilePath); } } jsSrcMainProvider .get() .genJsFiles( soyTreeClone, jsSrcOptions, locale, msgBundle, outputPathFormat, inputFilePathPrefix); } } }
/** * Generates Incremental DOM JS source code given a Soy parse tree, an options object, and an * optional bundle of translated messages. * * @param soyTree The Soy parse tree to generate JS source code for. * @param jsSrcOptions The compilation options relevant to this backend. * @return A list of strings where each string represents the JS source code that belongs in one * JS file. The generated JS files correspond one-to-one to the original Soy source files. * @throws SoySyntaxException If a syntax error is found. */ public List<String> genJsSrc(SoyFileSetNode soyTree, SoyJsSrcOptions jsSrcOptions) throws SoySyntaxException { // Generate code with the opt_ijData param if either (a) the user specified the compiler flag // --isUsingIjData or (b) any of the Soy code in the file set references injected data. boolean isUsingIjData = jsSrcOptions.isUsingIjData() || new IsUsingIjDataVisitor().exec(soyTree); // Make sure that we don't try to use goog.i18n.bidi when we aren't supposed to use Closure. Preconditions.checkState( !jsSrcOptions.getUseGoogIsRtlForBidiGlobalDir() || jsSrcOptions.shouldProvideRequireSoyNamespaces() || jsSrcOptions.shouldProvideRequireJsFunctions(), "Do not specify useGoogIsRtlForBidiGlobalDir without either" + " shouldProvideRequireSoyNamespaces or shouldProvideRequireJsFunctions."); try (WithScope withScope = apiCallScope.enter()) { // Seed the scoped parameters. apiCallScope.seed(SoyJsSrcOptions.class, jsSrcOptions); apiCallScope.seed(Key.get(Boolean.class, IsUsingIjData.class), isUsingIjData); BidiGlobalDir bidiGlobalDir = SoyBidiUtils.decodeBidiGlobalDirFromJsOptions( jsSrcOptions.getBidiGlobalDir(), jsSrcOptions.getUseGoogIsRtlForBidiGlobalDir()); ApiCallScopeUtils.seedSharedParams(apiCallScope, null /* msgBundle */, bidiGlobalDir); // TODO(sparhami) figure out how to deal with msg nodes - need to support some sort of // innerHTML, // which means we need autoescaping for just those subtrees. // new ReplaceMsgsWithGoogMsgsVisitor(errorReporter).exec(soyTree); // new MoveGoogMsgDefNodesEarlierVisitor(errorReporter).exec(soyTree); // Preconditions.checkState( // bidiGlobalDir != null, // "If enabling shouldGenerateGoogMsgDefs, must also set bidi global // directionality."); // Do the code generation. optimizeBidiCodeGenVisitorProvider.get().exec(soyTree); simplifyVisitor.exec(soyTree); new HtmlTransformVisitor(errorReporter).exec(soyTree); IncrementalDomOutputOptimizers.collapseOpenTags(soyTree); IncrementalDomOutputOptimizers.collapseElements(soyTree); return genIncrementalDomCodeVisitorProvider.get().exec(soyTree); } }
public void testMapLiteral() { // ------ Unquoted keys. ------ assertTranslation("[:]", new JsExpr("{}", Integer.MAX_VALUE)); assertTranslation( "['aaa': 123, 'bbb': 'blah']", new JsExpr("{aaa: 123, bbb: 'blah'}", Integer.MAX_VALUE)); assertTranslation( "['aaa': $foo, 'bbb': 'blah']", new JsExpr("{aaa: opt_data.foo, bbb: 'blah'}", Integer.MAX_VALUE)); // ------ Quoted keys. ------ assertTranslation("quoteKeysIfJs([:])", new JsExpr("{}", Integer.MAX_VALUE)); assertTranslation( "quoteKeysIfJs( ['aaa': $foo, 'bbb': 'blah'] )", new JsExpr("{'aaa': opt_data.foo, 'bbb': 'blah'}", Integer.MAX_VALUE)); assertTranslation( "quoteKeysIfJs(['aaa': 123, $boo: $foo])", new JsExpr( "(function() { var map_s = {'aaa': 123};" + " map_s[soy.$$checkMapKey(opt_data.boo)] = opt_data.foo;" + " return map_s; })()", Integer.MAX_VALUE)); assertTranslation( "quoteKeysIfJs([$boo: $foo, $goo[0]: 123])", new JsExpr( "(function() { var map_s = {};" + " map_s[soy.$$checkMapKey(opt_data.boo)] = opt_data.foo;" + " map_s[soy.$$checkMapKey(gooData8[0])] = 123;" + " return map_s; })()", Integer.MAX_VALUE)); // ------ Errors. ------ // Non-string key is error. assertSoyErrors( "[0: 123, 1: 'oops']", "Keys in map literals cannot be constants (found constant '0').", "Keys in map literals cannot be constants (found constant '1')."); SoyJsSrcOptions jsSrcOptionsWithoutCompiler = new SoyJsSrcOptions(); SoyJsSrcOptions jsSrcOptionsWithCompiler = new SoyJsSrcOptions(); jsSrcOptionsWithCompiler.setShouldGenerateJsdoc(true); // Non-identifier key without quoteKeysIfJs() is error only when using Closure Compiler. assertTranslation( "['0': 123, '1': $foo]", new JsExpr("{'0': 123, '1': opt_data.foo}", Integer.MAX_VALUE), jsSrcOptionsWithoutCompiler); assertSoyErrors( "['0': 123, '1': '123']", jsSrcOptionsWithCompiler, "Map literal with non-identifier key '0' must be wrapped in quoteKeysIfJs().", "Map literal with non-identifier key '1' must be wrapped in quoteKeysIfJs()."); // Expression key without quoteKeysIfJs() is error only when using Closure Compiler. assertTranslation( "['aaa': 123, $boo: $foo]", new JsExpr( "(function() { var map_s = {aaa: 123};" + " map_s[soy.$$checkMapKey(opt_data.boo)] = opt_data.foo;" + " return map_s; })()", Integer.MAX_VALUE), jsSrcOptionsWithoutCompiler); assertSoyErrors( "['aaa': 123, $boo: $foo]", jsSrcOptionsWithCompiler, "Expression key '$boo' in map literal must be wrapped in quoteKeysIfJs()."); }